Understanding Quartz Triggers

Jobs contain the logic for the task to perform, but a job knows nothing about the time that it should be executed. This knowledge is left for the trigger. A Quartz trigger extends the abstract org.quartz.Trigger class. Currently, three Quartz triggers are available:

  • org.quartz.SimpleTrigger
  • org.quartz.CronTrigger
  • org.quartz.NthIncludedDayTrigger

There is a fourth trigger, called UICronTrigger, but it has been deprecated in Quartz 1.5. It was primarily used by the Quartz Web application and is not used within Quartz itself.

Using the org.quartz.SimpleTrigger

Appropriately named, the SimpleTrigger is the simplest Quartz trigger to set up and use. It's designed to be used for jobs that need to start at a particular date/time and to repeat n number of times with a possible delay between each execution. Listing 4.9 provides an example that uses a SimpleTrigger.

Listing 4.9. Using a SimpleTrigger to Schedule a Job

public class Listing_4_9 {
 static Log logger = LogFactory.getLog(Listing_4_9.class);

 public static void main(String[] args) {
 Listing_4_9 example = new Listing_4_9();
 example.startScheduler();
 }

 public void startScheduler() {
 try {
 // Create and start the scheduler
 Scheduler scheduler =
 StdSchedulerFactory.getDefaultScheduler();
 scheduler.start();
 logger.info("Scheduler has been started");

 JobDetail jobDetail =
 new JobDetail("PrintInfoJob",
 Scheduler.DEFAULT_GROUP,
 PrintInfoJob.class);
 /*
 * Create a SimpleTrigger that starts immediately,
 * with a null end date/time, repeats forever and has
 * 1 minute (60000 ms) between each firing.
 */
 Trigger trigger =
 new SimpleTrigger("myTrigger",
 Scheduler.DEFAULT_GROUP, new Date(), null,
 SimpleTrigger.REPEAT_INDEFINITELY,
 60000L);

 scheduler.scheduleJob(jobDetail, trigger );

 } catch (SchedulerException ex) {
 logger.error(ex);
 }
 }
 }

Several variations of the SimpleTrigger constructor exist. They range from the no-argument version all the way to one that takes a full set of arguments. This code fragment shows a simple constructor that takes just the name and group for the trigger:

//No Argument Constructor
SimpleTrigger sTrigger =
 new SimpleTrigger("myTrigger", Scheduler.DEFAULT_GROUP);

This trigger will execute immediately and will not repeat. There's also a constructor that takes several arguments and configures the trigger to fire at a particular time, repeat multiple times, and delay between each firing.

public SimpleTrigger(String name, String group,
 String jobName, String jobGroup, Date startTime,
 Date endTime, int repeatCount, long repeatInterval);

 

Using the org.quartz.CronTrigger

The CronTrigger allows for a much more complex firing schedule. Whereas you might have to use two or more SimpleTriggers to meet your firing needs, you might need just a single CronTrigger instance.

As the name implies, the CronTrigger is based on UNIX cron-like expressions. For example, you might have a job that needs to be executed every five minutes between 8:00 AM and 9:00 AM on Monday and Friday. If you tried to implement this with a SimpleTrigger, you would probably end up with several triggers for the job. However, you can use an expression like this to produce a trigger that will fire on this schedule:

"0 0/5 8 ? * MON,FRI"
try {
 CronTrigger cTrigger = new CronTrigger("myTrigger",
 Scheduler.DEFAULT_GROUP, "0 0/5 8 ? *
MON,FRI");
} catch (ParseException ex) {
ex.printStackTrace();
}

Because CronTriggers have so much flexibility built into them by the nature of the almost limitless expressions that can be created, the next chapter focuses exclusively on everything you wanted to know about CronTriggers and cron expressions. Chapter 5, "CronTriggers and More," also presents a set of cookbook examples of how to create CronTriggers for specific firing schedules.

Using the org.quartz.NthIncludedDayTrigger

The org.quartz.NthIncludedDayTrigger is one of the newest triggers the Quartz development team added to the framework. It's designed to be used to execute a job on an nth day of every interval type. For example, if you needed to execute a job that performs invoicing on every 15th of the month, you could use NthIncludedDayTrigger to perform this feat. A Quartz Calendar can also be associated with the trigger to take weekends and holidays into account and move the days forward if necessary. The following fragment illustrates how to create a NthIncludedDayTrigger.

NthIncludedDayTrigger trigger =
 new NthIncludedDayTrigger(
 "MyTrigger", Scheduler.DEFAULT_GROUP);
 trigger.setN(15);
trigger.setIntervalType(
 NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);

 

Using Multiple Triggers for a Job

You are not forced to live with just a single trigger per job. If you need a more complex firing schedule, you can create multiple triggers and assign them to the same job. The Scheduler determines the proper execution schedule based on all the triggers for the job. Using multiple triggers for the same JobDetail is shown in the following method fragment:

try {
 // Create and start the scheduler
 Scheduler scheduler =
 StdSchedulerFactory.getDefaultScheduler();
 scheduler.start();
 logger.info("Scheduler has been started");

 JobDetail jobDetail =
 new JobDetail("PrintInfoJob",
 Scheduler.DEFAULT_GROUP,
 PrintInfoJob.class);

 // A trigger that fires every 5 seconds
 Trigger trigger1 =
 TriggerUtils.makeSecondlyTrigger("trigger1",
 5000, SimpleTrigger.REPEAT_INDEFINITELY);

 // A trigger that fires every 10 minutes
 Trigger trigger2 =
 TriggerUtils.makeMinutelyTrigger("trigger2", 10,
 SimpleTrigger.REPEAT_INDEFINITELY);

 // Schedule job with first trigger
 scheduler.scheduleJob(jobDetail, trigger1);

 // Schedule job with second trigger
 scheduler.scheduleJob(jobDetail, trigger1);

 } catch (SchedulerException ex) {
 logger.error(ex);
 }

One Job per Trigger

Although a single JobDetail can support multiple triggers, a trigger can be assigned to only a single job.

 

The Quartz Calendar

Don't confuse the Quartz Calendar object with the java.util.Calendar in the Java API. They are two different components and are used for two different purposes. As you are probably aware, Java's Calendar object is used for general-purpose Date and Time utilities; much of the functionality that used to reside in Java's Date class now resides within the Calendar classes.

The Quartz Calendar, on the other hand, is used exclusively to block out sections of time so that triggers are prevented from firing during those blocked-out periods. For example, let's assume that you work for a financial institution such as a bank. It's very common for banks to have many "bank holidays." Suppose you don't need (or want) the jobs to run on those days. You can accomplish this in one of several ways:

  • Let your jobs run anyway. (This might mess things up for the bank.)
  • Manually stop your jobs during the holidays. (Someone would need to be there to do it.)
  • Create multiple triggers that don't include these days. (This would be time-consuming to set up and maintain.)
  • Set up a bank holiday calendar that excludes these days. (Very easy to do!)

Although you could use each of these solutions to solve the problem, the Quartz Calendar is specifically designed for this.

The org.quartz.Calendar Interface

Quartz defines the org.quartz.Calendar interface that all Quartz Calendars must implement. It contains several methods, but these are the two most important ones:

public long getNextIncludedTime(long timeStamp);
public boolean isTimeIncluded(long timeStamp);

Granularity of Calendar Excluded Times

The parameter types for the Calendar interface are of type Long. This means that the Quartz Calendar is capable of excluding times down to the millisecond level. You will most likely never need to get that granular because most jobs exclude particular days or maybe hours. If you need to exclude at the millisecond level, however, this capability is there for you.

As a Quartz job creator and developer, you don't necessarily need to be that familiar with the Calendar interface. It's mainly for situations for which the existing Calendars (those that come with Quartz) are not sufficient. Out of the box, Quartz includes many Calendar implementations that should fill your needs. Table 4.1 lists the Calendars that come with Quartz ready to use.

Table 4.1. Quartz Includes Many Calendar Types That Your Applications Can Use

Calendar Name

Class

Usage

BaseCalendar

org.quartz.impl.calendar.BaseCalendar

Implements base functionality for more advanced Calendars. Implements the org.quartz.Calendar interface.

WeeklyCalendar

org.quartz.impl.calendar.WeeklyCalendar

Excludes one or more days of the weekfor example, can be used to exclude weekends.

MonthlyCalendar

org.quartz.impl.calendar.MonthlyCalendar

Excludes days of the monthfor example, can be used to exclude the last day of every month.

AnnualCalendar

org.quartz.impl.calendar.AnnualCalendar

Excludes one or more days during the year.

HolidayCalendar

org.quartz.impl.calendar.HolidayCalendar

Made especially to exclude holidays from the trigger.

 

Using Quartz Calendars

To use a Quartz Calendar, you simply need to instantiate one, add your excluded dates to it, and then register it with the Scheduler. Finally, associate the Calendar instance with each trigger instance that you want to use the Calendar with.

Using a Calendar for Multiple Jobs

You can't schedule the Calendar for all jobs just by adding it to the Scheduler. You need to associate the Calendar instance with each trigger. Adding the Calendar instance to the Scheduler allows it to be stored only with the JobStore in use; you must attach the Calendar to the trigger instance.

Listing 4.10 shows an example of excluding bank holidays using the Quartz AnnualCalendar class.

Listing 4.10. Using the AnnualCalendar to Exclude Bank Holidays

public class Listing_4_10 {
 static Log logger = LogFactory.getLog(Listing_4_10.class);

 public static void main(String[] args) {
 Listing_4_10 example = new Listing_4_10();
 example.startScheduler();
 }

 public void startScheduler() {
 try {
 // Create and start the scheduler
 Scheduler scheduler =
 StdSchedulerFactory.getDefaultScheduler();
 scheduler.start();

 scheduleJob(scheduler, PrintInfoJob.class);

 logger.info("Scheduler starting up...");
 scheduler.start();

 } catch (SchedulerException ex) {
 logger.error(ex);
 }
 }

 private void scheduleJob(Scheduler scheduler, Class jobClass) {
 try {
 // Create an instance of the Quartz AnnualCalendar
 AnnualCalendar cal = new AnnualCalendar();

 // exclude July 4th
 Calendar gCal = GregorianCalendar.getInstance();
 gCal.set(Calendar.MONTH, Calendar.JULY);
 gCal.set(Calendar.DATE, 4);

 cal.setDayExcluded(gCal, true);

 // Add to scheduler, replace existing, update triggers
 scheduler.
 addCalendar("bankHolidays", cal, true, true);

 /*
 * Set up a trigger to start firing now, repeat forever
 * and have (60000 ms) between each firing.
 */
 Trigger trigger =
 TriggerUtils.makeImmediateTrigger("myTrigger",
 -1,60000);

 // Trigger will use Calendar to exclude firing times
 trigger.setCalendarName("bankHolidays");

 JobDetail jobDetail =
 new JobDetail(jobClass.getName(),
 Scheduler.DEFAULT_GROUP, jobClass);

 // Associate the trigger with the job in the scheduler
 scheduler.scheduleJob(jobDetail, trigger);

 } catch (SchedulerException ex) {
 logger.error(ex);
 }
 }
}

When you run the example from Listing 4.10, unless it's July 4, you should see the job execute. As an exercise that's left for you to do, change the excluded date in the scheduleJob() method to the current date that you're reading this. If you run the code again, you should see that the current date has been excluded and the next firing time will be tomorrow.

Why Didn t We Use HolidayCalendar?

You might be wondering why we didn't choose to use the HolidayCalendar in the previous example. The HolidayCalendar class takes year into account. So if you wanted to exclude July 4 for the next 3 years, you would need to add each one of those entries in as an excluded date. AnnualCalendar simply excludes dates for every year and was much easier to use in this case.

 

Creating Your Own Calendars

This last section demonstrates how easy it is to create your own Calendar class. Suppose that you need a Calendar to exclude certain minutes of the hour. For example, suppose you need to exclude the first five minutes of every hour or the last 15 minutes of every hour. You can create a new Calendar to support this functionality.

We Could Probably Use a CronTrigger

We could probably come up with a cron expression to exclude these times, but that takes the fun out of creating a new Calendar class.

Listing 4.11 shows the HourlyCalendar that we can use to exclude sets of minutes from the hour.

Listing 4.11. The HourlyCalendar Can Exclude Certain Minutes from Every Hour

public class HourlyCalendar extends BaseCalendar {

 // Array of Integer from 0 to 59
 private List excludedMinutes = new ArrayList();

 public HourlyCalendar() {
 super();
 }
 public HourlyCalendar(Calendar baseCalendar) {
 super(baseCalendar);
 }

 public List getMinutesExcluded() {
 return excludedMinutes;
 }

 public boolean isMinuteExcluded(int minute) {

 Iterator iter = excludedMinutes.iterator();
 while (iter.hasNext()) {
 Integer excludedMin = (Integer) iter.next();

 if (minute == excludedMin.intValue()) {
 return true;
 }

 continue;
 }
 return false;
 }

 public void setMinutesExcluded(List minutes) {
 if (minutes == null)
 return;

 excludedMinutes.addAll(minutes);
 }

 public void setMinuteExcluded(int minute) {

 if (isMinuteExcluded(minute))
 return;

 excludedMinutes.add(new Integer(minute));
 }

 public boolean isTimeIncluded(long timeStamp) {

 if (super.isTimeIncluded(timeStamp) == false) {
 return false;
 }

 java.util.Calendar cal = getJavaCalendar(timeStamp);
 int minute = cal.get(java.util.Calendar.MINUTE);

 return !(isMinuteExcluded(minute));
 }

 public long getNextIncludedTime(long timeStamp) {
 // Call base calendar implementation first
 long baseTime = super.getNextIncludedTime(timeStamp);
 if ((baseTime > 0) && (baseTime > timeStamp))
 timeStamp = baseTime;

 // Get timestamp for 00:00:00
 long newTimeStamp = buildHoliday(timeStamp);
 java.util.Calendar cal = getJavaCalendar(newTimeStamp);
 int minute = cal.get(java.util.Calendar.MINUTE);

 if (isMinuteExcluded(minute) == false)
 return timeStamp; // return the
 // original value

 while (isMinuteExcluded(minute) == true) {
 cal.add(java.util.Calendar.MINUTE, 1);
 }
 return cal.getTime().getTime();
 }
}

If you use the HourlyCalendar to schedule a job, all you need to do is set the minutes of the hour that you want to exclude; the Calendar and the Scheduler do the rest. You can see the HourlyCalendar demonstrated in Listing 4.12.

Listing 4.12. The HourlyCalendar Executes Based on Certain Excluded Minutes of the Hour

public class Listing_4_12 {
 static Log logger = LogFactory.getLog(Listing_4_12.class);

 public static void main(String[] args) {
 Listing_4_12 example = new Listing_4_12();
 example.startScheduler();
 }

 public void startScheduler() {
 try {
 // Create a default instance of the Scheduler
 Scheduler scheduler =
 StdSchedulerFactory.getDefaultScheduler();

 // Using the NoOpJob, but could have been any
 scheduleJob(scheduler, PrintInfoJob.class);

 logger.info("Scheduler starting up...");
 scheduler.start();

 } catch (SchedulerException ex) {
 logger.error(ex);
 }
 }

 private void scheduleJob(Scheduler scheduler, Class jobClass) {
 try {
 // Create an instance of the Quartz AnnualCalendar
 HourlyCalendar cal = new HourlyCalendar();
 cal.setMinuteExcluded(47);
 cal.setMinuteExcluded(48);
 cal.setMinuteExcluded(49);
 cal.setMinuteExcluded(50);

 // Add Calendar to the Scheduler
 scheduler.
 addCalendar("hourlyExample", cal, true, true);

 Trigger trigger =
 TriggerUtils.makeImmediateTrigger("myTrigger",
 -1, 10000);

 // Trigger will use Calendar to exclude firing times
 trigger.setCalendarName("hourlyExample");

 JobDetail jobDetail =
 new JobDetail(jobClass.getName(),
 Scheduler.DEFAULT_GROUP, jobClass);

 // Associate the trigger with the job in the scheduler
 scheduler.scheduleJob(jobDetail, trigger);

 } catch (SchedulerException ex) {
 logger.error(ex);
 }
 }
 }

When you run Listing 4.12, you should see that the PrintInfoJob is not executed during the excluded minutes. Change the minutes that are excluded using the setMinuteExcluded() method and see for yourself how the new Calendar works.


Scheduling in the Enterprise

Getting Started with Quartz

Hello, Quartz

Scheduling Jobs

Cron Triggers and More

JobStores and Persistence

Implementing Quartz Listeners

Using Quartz Plug-Ins

Using Quartz Remotely

Using Quartz with J2EE

Clustering Quartz

Quartz Cookbook

Quartz and Web Applications

Using Quartz with Workflow

Appendix A. Quartz Configuration Reference



Quartz Job Scheduling Framework(c) Building Open Source Enterprise Applications
Quartz Job Scheduling Framework: Building Open Source Enterprise Applications
ISBN: 0131886703
EAN: 2147483647
Year: N/A
Pages: 148

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net