Listening for Job Events
The org.quartz.JobListener interface contains a set of methods that are invoked when certain key events occur in the life cycle of a job. The methods available for JobListener are shown in Listing 7.1.
Listing 7.1. Methods of the org.quartz.JobListener Interface
public interface JobListener { public String getName(); public void jobToBeExecuted(JobExecutionContext context); public void jobExecutionVetoed(JobExecutionContext context); public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException); } |
The methods in the JobListener interface are fairly self-explanatory. However, we should make a few points about them.
The getName() Method
The getName() method returns a String that represents the name of the JobListener. For listeners that are registered as global, the getName() method is primarily used for logging. However, for JobListeners that are referenced by specific jobs, the listener name registered with the JobDetail must match the value returned from the getName() method on the listener instance. This will be more obvious after you see a few examples.
The jobToBeExecuted() Method
The Scheduler calls this method when the JobDetail is about to be executed.
The jobExecutionVetoed() Method
The Scheduler calls this method when the JobDetail was about to be executed but a triggerListener vetoed the execution.
The jobWasExecuted() Method
The Scheduler calls this method after the JobDetail is executed.
Listing 7.2 shows a very simple JobListener implementation.
Listing 7.2. A Simplified JobListener Implementation
package org.cavaness.quartzbook.chapter7; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobListener; public class SimpleJobListener implements JobListener { Log logger = LogFactory.getLog(SimpleJobListener.class); public String getName() { return getClass().getSimpleName(); } public void jobToBeExecuted(JobExecutionContext context) { String jobName = context.getJobDetail().getName(); logger.info(jobName + " is about to be executed"); } public void jobExecutionVetoed(JobExecutionContext context) { String jobName = context.getJobDetail().getName(); logger.info(jobName + " was vetoed and not executed()"); } public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { String jobName = context.getJobDetail().getName(); logger.info(jobName + " was executed"); } } |
The JobListener in Listing 7.2 prints a log message, which, obviously, is a basic use of the Listener. The logic that you implement is entirely up to you and your application needs. You might want to send an e-mail when a job finishes successfully or schedule another job after one gets vetoedyou have the freedom to perform just about any action from within the callback method.
Earlier we stated that JobListeners (and triggerListeners) can be either global or nonglobal. Notice that we didn't have to know in advanced whether the JobListener in Listing 7.2 was a global or nonglobal listener: We just implemented the interface and provided the listener methods. Listing 7.3 illustrates how to use the SimpleJobListener from Listing 7.2 and register it as global JobListener.
Listing 7.3. Using the SimpleJobListener as a Global JobListener
package org.cavaness.quartzbook.chapter7; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cavaness.quartzbook.common.PrintInfoJob; import org.quartz.JobDetail; import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_7_3 { static Log logger = LogFactory.getLog(Listing_7_3.class); public static void main(String[] args) { Listing_7_3 example = new Listing_7_3(); try { example.startScheduler(); } catch (SchedulerException ex) { logger.error(ex); } } public void startScheduler() throws SchedulerException { // Create an instance of the factory Scheduler scheduler = null; // Create the scheduler and JobDetail scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = new JobDetail("PrintInfoJob", Scheduler.DEFAULT_GROUP, PrintInfoJob.class); /* * Set up a trigger to start firing now, with no end * date/time, repeat forever and have * 10 secs (10000 ms) between each firing. */ Trigger trigger = TriggerUtils.makeSecondlyTrigger(10); trigger.setName("SimpleTrigger"); trigger.setStartTime(new Date()); // Register the JobDetail and Trigger scheduler.scheduleJob(jobDetail, trigger); // Create and register the global job listener JobListener jobListener = new SimpleJobListener("SimpleJobListener"); scheduler.addGlobalJobListener(jobListener); // Start the scheduler scheduler.start(); logger.info("Scheduler was started at " + new Date()); } } |
The code in Listing 7.3 should be pretty straightforward by now. A JobDetail and trigger are created and registered with a Scheduler instance, just as we've done many times before.
A SimpleJobListener from Listing 7.2 is instantiated and registered with the Scheduler as a global JobListener by calling the addGlobalJobListener() method. Finally, the Scheduler is started.
Because we set up a single job (the PrintInfoJob), we get callbacks for only that JobDetail. However, if we had scheduled another job, we would also see callback log messages for the second job because the listener was configured as a global listener.
Registering Nonglobal JobListeners
You can also use the SimpleJobListener from Listing 7.2 as a nonglobal JobListener. To do this, you would only need to modify the code in the startScheduler() method from Listing 7.3. Listing 7.4 illustrates the small change that would need to be made.
Listing 7.4. Using the SimpleJobListener as a Nonglobal JobListener
package org.cavaness.quartzbook.chapter7; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cavaness.quartzbook.common.PrintInfoJob; import org.quartz.JobDetail; import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_7_4 { static Log logger = LogFactory.getLog(Listing_7_4.class); public static void main(String[] args) { Listing_7_4 example = new Listing_7_4(); try { example.startScheduler(); } catch (SchedulerException ex) { logger.error(ex); } } public void startScheduler() throws SchedulerException { Scheduler scheduler = null; try { // Create the scheduler and JobDetail scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = new JobDetail("PrintInfoJob", Scheduler.DEFAULT_GROUP, PrintInfoJob.class); /* * Set up a trigger to start firing now, with no end * date/time, repeat forever and have * 10 secs (10000 ms) between each firing. */ Trigger trigger = TriggerUtils.makeSecondlyTrigger(10); trigger.setName("SimpleTrigger"); trigger.setStartTime(new Date()); // Create the job listener JobListener jobListener = new SimpleJobListener("SimpleJobListener"); // Register Listener as a nonglobal listener scheduler.addJobListener(jobListener); // Listener set on JobDetail before scheduleJob() jobDetail.addJobListener(jobListener.getName()); // Register the JobDetail and Trigger scheduler.scheduleJob(jobDetail, trigger); // Start the scheduler scheduler.start(); logger.info("Scheduler started at " + new Date()); } catch (SchedulerException ex) { logger.error(ex); } } } |
Listing 7.4 is very similar to the code from Listing 7.3. Because the JobListener is being registered as a nonglobal listener, you have to call the addJobListener() method on the Scheduler instead of the addGlobalJobListener() method. For a nonglobal JobListener, it should be added to the Scheduler before any JobDetail that references it is added using the schedulerJob() or addJob() methods.
Next, the name of the JobListener is set on the JobDetail. Notice that the JobListener instance is not set; just the name is. This is done by calling the addJobListener() method and passing in the name. The name passed into the addJobListener() method must match the name returned from the listener getName() method. If the Scheduler can't find a listener with that name, it throws a SchedulerException.
Finally, the Scheduler is started.
Listening for Trigger Events
|