Spring Boot Quartz Scheduler Example

Quartz is an open-source Job scheduler that we can leverage to schedule enterprise-grade jobs. Spring boot also supports scheduling Quartz Jobs by providing the starter dependency. In this article, we will learn how to create a simple quartz scheduler along with the spring boot framework.

We will create a spring boot application with a REST API that accepts a request parameter. We will dynamically schedule the quartz job by invoking the API. Also, We will pass the request parameter to the job that prints the value on the application’s console.

The quartz scheduler also supports persisting the scheduler and job details into a database. This is an useful feature as the scheduler can handle any failure or misfire of the job.

We will also enable clustering of jobs so that the spring quartz application supports scaling without any issues.

We will use the PostgreSQL database along with spring data JPA.

Version details

  • Spring boot version : 2.7.2

Table of Contents

Using spring boot quartz dependency

Spring boot provides the spring-boot-starter-quartz starter dependency that we can leverage to create quartz schedulers to schedule a job.

We also have the spring data jpa and postgresql dependencies, as shown below.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
</dependency>

Creating a Job class

Create a Job class as shown below.

The class implements execute() method of the Job interface, and this method is the starting point of the Quartz scheduler job.

@Slf4j
public class MyMessagePrinter implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();
        String message = jobDataMap.get("message").toString();
        log.info("Scheduler is printing message:: {}", message);
    }
}

The method has a parameter of type JobExecutionContext, which is helpful in retrieving the job data and other details that we can add while scheduling the Quartz job.

In our example, we are retrieving the job data with the key message and logging the message.

Configuring the Quartz scheduler

To override the default configuration, add the below properties to the application.yml configuration file.

spring:
  datasource:
    url: jdbc:postgresql://localhost/postgres
    username: postgres
    password: mysecretpassword
    pool-size: 10
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always
    properties:
      org:
        quartz:
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 5
          jobStore:
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            dataSource: spring.datasource
            driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
            isClustered: true
          scheduler:
            instanceName: message-printer
            instanceId: AUTO

First, we are configuring the spring data source with the PostgreSQL database.

Then, we are configuring the quartz-specific configuration to support clustered and persistent jobs.

We are specifying the quartz job-store-type as jdbc. It overrides the default RAMJobStore in-memory job store.

The quartz initialize-scheme property initializes all the required database tables that quartz uses to persist the scheduler and job details.

We can also use the spring.quartz.properties configuration prefix to set quartz-specific configurations like thread pool, etc.

The quartz job store class is set to LocalDataSourceJobStore, along with the data source property, so that quartz uses the available spring data source.

We can use the isClustered flag to enable the clustering. Also, it is better to set the instanceId property value to AUTO so that the application generates a random instance ID for every new application instance.

Scheduling the Job

In this example, we will schedule the job dynamically using a REST endpoint.

If necessary, we can also define them as spring beans so that job gets scheduled during the application startup.

Create a SchedulerController.java class and a /schedule-job REST API as shown below.

@Slf4j
@RestController
public class SchedulerController {

    @Autowired
    private Scheduler scheduler;

    @GetMapping("/schedule-job")
    public String scheduleJob(@RequestParam("msg") String message) throws SchedulerException {

        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("message", message);

        JobKey jobKey = new JobKey("MessagePrintingJob");

        JobDetail jobDetail = JobBuilder.newJob()
                .withIdentity(UUID.randomUUID().toString())
                .setJobData(jobDataMap)
                .withDescription("Simple message printing Job.")
                .ofType(MyMessagePrinter.class)
                .storeDurably()
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(jobDetail)
                .startAt(Date.from(Instant.now().plus(30, ChronoUnit.SECONDS)))
                .withDescription("Simple message printing Job trigger.")
                .build();

        log.info("Scheduling printing message Job with message :{}", message);
        scheduler.scheduleJob(jobDetail, trigger);
        return "Job scheduled!!";
    }
}

The API accepts a request parameter with the name msg. We will use the value of this parameter as job data.

The main parts of the quartz scheduler are Job, Trigger, and Scheduler.

Job

The Job contains the details about the job execution.

Every quartz job can have a unique job key. We are creating a job key with the name MessagePrintingJob.

We can use JobBuilder class to create a new job along with its properties such as durability, job identity, job data, etc.

Also, we can use the ofType() method to specify the job class to invoke during job execution. We created this class in the initial part of the article.

Trigger

Once the JobDetail is ready, we need to configure the Trigger. Trigger specifies when a job is triggered.

Quarts provides TriggerBuilder class that we can use to create an instance of the trigger. Quartz supports different triggers like CRON triggers, simple triggers, etc.

Scheduler

The Scheduler is responsible for scheduling the Job with provided job details and trigger information.

We are using spring boot’s auto-configured Scheduler instance to schedule our message printing job.

Testing the application

Run the application we should see the scheduler initialized message as shown below. Scheduler supports persistence and clustering.

spring boot quartz scheduler

The application also initializes the database tables required for the quartz scheduler.

spring boot quartz scheduler example

Invoke the API, and we can observe that the job is scheduled to print the requested message.

spring boot quartz job

Once the request is received, the scheduler schedules the job to print the message after 30 seconds.

quartz spring boot job running

The job details also get persisted in the database, as shown below.

quartz job data persisted
quartz job data persisted
quartz job data persisted

Conclusion

In this article, we learned how to schedule the job using the Quartz scheduler and spring boot.

We also learned how to enable clustering and job persistence for quartz schedulers.

Example code is available on Github.

Leave a Reply