Monday, November 29, 2010

Quartz Scheduler 2.0 Beta 1 Welcomes New Fluent API and "Where"

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:

//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();
view raw gistfile1.java hosted with ❤ by GitHub

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.

//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);
view raw gistfile1.java hosted with ❤ by GitHub

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.

//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);
view raw gistfile1.java hosted with ❤ by GitHub

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.


No comments: