Quartz Scheduler, the most widely used Java scheduler is getting some major improvements for 2.0. I'm going to talk a little about what to expect but you can also read the release notes here.
Goals
We had two major goals when planning for Quartz 2.0 began.
- Simplify/modernize the Quartz API.
- Improve the Quartz experience when leveraging a cluster
Quart 2.0 API
In order to improve the usability and readability of Quartz 2.0 over passed versions we spent a lot of time evaluating the parts and how a user interacts with them. We found that too much knowledge of those parts needed to be understood at construction time. As a result we moved to a "Fluent API/DSL" approach. The best way to get a feel for the kind of improvement this gives is to compare the samples from 1.8.4 to the same ones translated in 2.0 Beta 1.
In the simple of case of example 1 from the Quartz Kit you can see the basic philosophy change:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Quartz 1.8.4 Example 2 | |
// First we must get a reference to a scheduler | |
SchedulerFactory sf = new StdSchedulerFactory(); | |
Scheduler sched = sf.getScheduler(); | |
log.info("------- Initialization Complete -----------"); | |
log.info("------- Scheduling Jobs -------------------"); | |
// computer a time that is on the next round minute | |
Date runTime = TriggerUtils.getEvenMinuteDate(new Date()); | |
// define the job and tie it to our HelloJob class | |
JobDetail job = new JobDetail("job1", "group1", HelloJob.class); | |
// Trigger the job to run on the next round minute | |
SimpleTrigger trigger = | |
new SimpleTrigger("trigger1", "group1", runTime); | |
// Tell quartz to schedule the job using our trigger | |
sched.scheduleJob(job, trigger); | |
// Quartz 2.0 Example 2 | |
SchedulerFactory sf = new StdSchedulerFactory(); | |
Scheduler sched = sf.getScheduler(); | |
log.info("------- Initialization Complete -----------"); | |
// computer a time that is on the next round minute | |
Date runTime = DateBuilder.evenMinuteDate(new Date()); | |
log.info("------- Scheduling Job -------------------"); | |
// define the job and tie it to our HelloJob class | |
JobDetail job = newJob(HelloJob.class) | |
.withIdentity("job1", "group1") | |
.build(); | |
// Trigger the job to run on the next round minute | |
Trigger trigger = newTrigger() | |
.withIdentity("trigger1", "group1") | |
.startAt(runTime) | |
.build(); | |
// Tell quartz to schedule the job using our trigger | |
sched.scheduleJob(job, trigger); | |
log.info(job.getKey() + " will run at: " + runTime); | |
// Start up the scheduler (nothing can actually run until the | |
// scheduler has been started) | |
sched.start(); |
Improvements include:
- The date/time related methods have been moved off of the Trigger and Job classes into a Date building class called "DateBuilder"
- We've removed the need to know details about which Job and Trigger classes you need and instead infer them through the building methods you call.
- The construction now reads more like a sentence. new job withIdentity "job1", "group1". new trigger withIdentity "trigger1", "group1" start at runTime
This is pretty subtle in a simple case like example 1 but gets more obvious as the cases get more complex. Let's look at example 2.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Quartz scheduler Example 2 from 1.8.3 | |
// job3 will run 11 times (run once and repeat 10 more times) | |
// job3 will repeat every 10 seconds (10000 ms) | |
job = new JobDetail("job3", "group1", SimpleJob.class); | |
trigger = new SimpleTrigger("trigger3", "group1", "job3", "group1", | |
new Date(ts), null, 10, 10000L); | |
ft = sched.scheduleJob(job, trigger); | |
// the same job (job3) will be scheduled by a another trigger | |
// this time will only run every 70 seocnds (70000 ms) | |
trigger = new SimpleTrigger("trigger3", "group2", "job3", "group1", | |
new Date(ts), null, 2, 70000L); | |
ft = sched.scheduleJob(job, trigger); | |
//Quartz scheduler Example 2 from 2.0 | |
// job3 will run 11 times (run once and repeat 10 more times) | |
// job3 will repeat every 10 seconds | |
job = newJob(SimpleJob.class) | |
.withIdentity("job3", "group1") | |
.build(); | |
trigger = newTrigger() | |
.withIdentity("trigger3", "group1") | |
.startAt(startTime) | |
.withSchedule(simpleSchedule() | |
.withIntervalInSeconds(10) | |
.withRepeatCount(10)) | |
.build(); | |
ft = sched.scheduleJob(job, trigger); | |
// the same job (job3) will be scheduled by a another trigger | |
// this time will only repeat twice at a 70 second interval | |
trigger = newTrigger() | |
.withIdentity("trigger3", "group2") | |
.startAt(startTime) | |
.withSchedule(simpleSchedule() | |
.withIntervalInSeconds(10) | |
.withRepeatCount(2)) | |
.forJob(job) | |
.build(); | |
ft = sched.scheduleJob(trigger); | |
In this case notice how the constructors are growing with no real indication to what each parameter means.
Now lets look at an example where you have to choose a specific Trigger type.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Quartz 1.8.4 Example 3 snippet | |
// job 7 will run every 30 seconds but only on Weekends (Saturday and | |
// Sunday) | |
job = new JobDetail("job7", "group1", SimpleJob.class); | |
trigger = new CronTrigger("trigger7", "group1", "job7", "group1", | |
"0,30 * * ? * SAT,SUN"); | |
sched.addJob(job, true); | |
ft = sched.scheduleJob(trigger); | |
//Quartz 2.0 Example 3 snippet | |
// job 7 will run every 30 seconds but only on Weekends (Saturday and Sunday) | |
job = newJob(SimpleJob.class) | |
.withIdentity("job7", "group1") | |
.build(); | |
trigger = newTrigger() | |
.withIdentity("trigger7", "group1") | |
.withSchedule(cronSchedule("0,30 * * ? * SAT,SUN")) | |
.build(); | |
ft = sched.scheduleJob(job, trigger); |
This example shows how in Quartz 1.8.4 you would have to know to select a different Trigger type but in 2.0 it's just abstracted away in the scheduling.
It's worth going through the samples yourself and making any suggestions you may have. Still plenty of time to get your API suggestions in via the Quartz Forum!
Quartz "Where" And Other Improvements
Clustered Quartz has gotten two major improvements in 2.0. Improved performance as node count increases and Quartz "Where"
I wrote a blog about this a while back but now you can play with it. Alex Snaps has written an excellent blog with code samples on the topic. Check it out to really dig in.