Working with Quartz Scheduler using Spring Boot
if you are working in Software Development for long period of time it’s most likely that you might have heard about quartz scheduler, yes as the name says it is used to schedule and execute a task/job at a specific date and time.
well if you have heard about quartz scheduler it’s impossible that you have not heard about spring boot scheduler, so what’s the difference between them and when to use which scheduler
before we get into any heated discussion about which scheduler to use either Spring Boot Scheduler or Quartz Scheduler let me highlight few things
as we know spring boot provides abstraction on most of the open source libraries available to make simplest use of them, same thing is true with Spring Boot Scheduler as well
Yes, Spring Boot Scheduler library uses Quartz Scheduler library internally for scheduling a task, if you look at the TaskScheduler or @Scheduled annotation for which spring creates a Scheduler object and this scheduler object is implemented using SchedulerFactoryBean which uses Quartz Library
so the main point here is instead of comparing which library is better, it’s best that we look into which one to use when i.e. either Spring Boot Scheduler or to use Quartz Library directly
Simply putting in words you can use Spring Boot Scheduler most of time for interval job execution
we can use Quartz Scheduler when
- when the in scheduling you have to execute task at a irregular interval
- when you have to decide the specific date and time to start the task
- you want misfire handling, job persistance etc.
on how to work with spring boot scheduler you can follow this blog : https://wankhedeshubham.medium.com/want-to-execute-task-on-scheduled-period-spring-boot-scheduler-f9c1e71ba165
now let’s understand how to directly work with Quartz Scheduler library using Spring Boot, before we get started let’s look at different components which we need or we can use while working with Quartz Scheduler
we are going to use QuartzScheduler to schedule a job/task.
the steps we need to follow and components we need to get started with most schedule a job is
- writing job store configuration
- Creating Job Bean
- Creating Trigger and Job Details
- Scheduler Job using Quartz Scheduler
let’s understand each steps
Step 1: Job Store Configuration
configure below configurations in application.properties
#store type either in-memory or jdbc to store in DB
spring.quartz.job-store-type=jdbc
p
#to create tables used to store job and other scheduling activities
spring.quartz.jdbc.initialize-schema=always
#datasource to store jobs in DB if store type is jdbc
spring.datasource.username=
spring.datasource.password=
spring.datasource.url=jdbc:h2:file:D:/H2_DB/quartz.db
spring.datasource.driver=org.h2.Driver
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# if you want to provide multiple threads to run jobs
spring.quartz.properties.org.quartz.threadPool.threadCount=5
let’s understand few of these configuration
spring.quartz.job-store-type=jdbc
property is used to define the job store we want to use in this case the value jdbc
represents that we want to store job, triggers etc in Datasource configured
we can also use value as memory
which is used to store job in in-memory
we need Quartz tables to store jobs in database we can use initilize property spring.quartz.jdbc.initialize-schema=always
to automatically create the tables required is used to automatic
if you want to create tables manually you can use the official SQL script provided by quartz (https://github.com/callicoder/spring-boot-quartz-scheduler-email-scheduling/blob/master/src/main/resources/quartz_tables.sql)
we can also configure the number of concurrent threads we can use using spring.quartz.properties.properties.properties.org.quartz.threadpool.threadCount=5
aside from this we have to configure DB properties where we are going to store the jobs it can H2 DB or others.
Step 2: Creating Job Bean
to create a job we have to create a .java file extends it with QuartzJobBean
@Component
public class RecordCreationJob extends QuartzJobBean {
@Autowired
private RecordRepository recordRepository;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
String dataPassed = context.getMergedJobDataMap().get("dataPassed").toString();
System.out.println("Data Passed From Job Details : " + dataPassed);
System.out.println("Print Records : ");
recordRepository.findAll().stream().forEach(System.out::println);
}
}
in executeInternal
method we have to write the logic we want to execute at scheduled period
Step 3: Creating Trigger and Job Details
we are going to create a service class from where we are going to schedule a job and since to schedule a job we need Trigger and Job Details we are going to create them in service class only
JobDetails
public JobDetail buildJobDetails(){
//create map to store key-value (can be received from request) which can be passed to job
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("dataPassed", "Data can be passed to job it can be request Data or other" );
return JobBuilder.newJob(RecordCreationJob.class)
.withIdentity(UUID.randomUUID().toString(),"record-jobs") // unique job key
.withDescription("Record Job Description")
.usingJobData(jobDataMap) // values to pass to job
.storeDurably(true) //false - delete job once job executed
.build();
}
JobDataMap contains some properties we want to pass to our job which we are going to execute on schedule, these properties can be received from request when we are scheduling an event.
Identity of Job need to unique so as to uniquely identify them in table
if we want to delete the job from Job Store once we are done executing it then we can keep storeDurably(false)
Trigger
public Trigger buildTrigger(JobDetail jobDetail){
ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDate.parse("2023-04-01"),
LocalTime.of(8,30), ZoneId.of("Asia/Kolkata"));
return TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(jobDetail.getKey().getName(),"records-tigger")
.withIdentity("Records Trigger Description")
.startAt(Date.from(zonedDateTime.toInstant()))
/*
// Some Scheduler Example
.withSchedule(impleScheduleBuilder.simpleSchedule().withIntervalInHours(1))
--> cron scheduler
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 16 * * ?")
.withMisfireHandlingInstructionFireAndProceed())
*/
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.build();
}
since the job can be executed in any time zone we are providing the zoned time to start time of job
Note: one job can have multiple trigger but one trigger can only have one job
we can schedule job using cron expression or in interval for Quartz library has provided different schedules
Step 4: Scheduler Job using Quartz Scheduler
last but not least is to schedule a job using JobDetails and Trigger
@Service
public class RecordService {
@Autowired
private Scheduler quartzScheduler;
public void scheduleRecordPrinter(){
try {
JobDetail jobDetail = .buildJobDetails();
Trigger trigger = buildTrigger(jobDetail);
quartzScheduler.scheduleJob(jobDetail,trigger);
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
//Implementation of trigger and jobdetails are mentioned above
public Trigger buildTrigger(JobDetail jobDetail){ .... }
public JobDetail buildJobDetails(){ ... }
}
here you can see I’ve autowired the Scheduler spring bean which is QuartzScheduler
implementation
we need to use scheduleJob
method and then pass trigger and job detail to schedule a job
Calling Scheduler
now we can call this scheduler from main method or if you are scheduling an event and are passing details start date and job specific data we call it from rest endpoint method
we are going to test it from main method
SpringBootApplication
public class QuartzSchedulerApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(QuartzSchedulerApplication.class, args);
RecordService recordService = context.getBean(RecordService.class);
recordService.scheduleRecordPrinter();
}
}
with this we are done with simple quartz scheduler application.