This section provides examples for working with Quartz jobs.
Creating a New Job Class
Creating a new job class is simple. Just create a class that implements the org.quartz.Job interface. This interface requires that you implement the execute() method, which is called when the Scheduler determines that the job should execute.
Listing 12.4 demonstrates a simple job that checks a mail server for new mail messages for a specific user. When the Scheduler executes this job, the execute() method is called, and the code connects to a mail server and gets any mail messages. This job simply prints who the message is from and the subject of the mail message.
Listing 12.4. A Quartz Job That Checks a Mail Server for Mail Messages
package org.cavaness.quartzbook.chapter12; import java.security.NoSuchProviderException; import java.util.Properties; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Store; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class CheckEmailJob implements Job { String mailHost = "some.mail.host"; String username = "aUsername"; String password = "aPassword"; // Default Constructor public CheckEmailJob() { super(); } public void execute(JobExecutionContext context) throws JobExecutionException { checkMail(); } protected void checkMail() { // Get session Session session = null; try { // Get system properties Properties props = System.getProperties(); session = Session.getDefaultInstance(props, null); // Get the store Store store = session.getStore("pop3"); store.connect(mailHost, username, password); // Get folder Folder folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); // Get directory Message message[] = folder.getMessages(); int numOfMsgs = message.length; if (numOfMsgs > 0) { for (int i = 0, n = numOfMsgs; i < n; i++) { System.out.println("(" + i + " of " + numOfMsgs + "): " + message[i].getFrom()[0] + " " + message[i].getSubject()); } } else { System.out.println("No Messages for user"); } // Close connection folder.close(false); store.close(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { CheckEmailJob job = new CheckEmailJob(); job.checkMail(); } } |
Most of Listing 12.4 involves using the JavaMail API to access the mail server. In terms of implementing a new Quartz job class, very little has to be done. Essentially, you implement the job interface and the execute() method, and it's ready to be scheduled. This is shown in the next example.
Scheduling a Quartz Job
As the previous example demonstrated, it's pretty straight-forward to create a Quartz job. Fortunately, configuring a job with the Scheduler isn't much more difficult. Listing 12.5 shows an example of scheduling the CheckEmailJob from the previous listing.
Listing 12.5. Example Showing How to Schedule the CheckEmailJob
package org.cavaness.quartzbook.chapter12; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_12_5 { static Log logger = LogFactory.getLog(Listing_12_5.class); public static void main(String[] args) { Listing_12_5 example = new Listing_12_5(); example.runScheduler(); } public void runScheduler() { Scheduler scheduler = null; try { // Get a Scheduler instance from the Factory scheduler = StdSchedulerFactory.getDefaultScheduler(); // Start the scheduler scheduler.start(); // Create a JobDetail for the Job JobDetail jobDetail = new JobDetail("CheckEmailJob", Scheduler.DEFAULT_GROUP, CheckEmailJob.class); // Create a trigger that fires every 1 hour Trigger trigger = TriggerUtils.makeHourlyTrigger(); trigger.setName("emailJobTrigger"); // Start the trigger firing from now // trigger.setStartTime(new Date()); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException ex) { // deal with any exceptions logger.error(ex); } } } |
The code in Listing 12.5 obtains the Scheduler from the StdSchedulerFactory and starts it. It then creates a JobDetail for the CheckEmailJob and creates a trigger for the job to fire every hour, starting immediately.
Firing a Job One Time
The org.quartz.TriggerUtils class is convenient and contains many useful methods. One of the most useful methods is the one that can schedule a fire-once immediate trigger. Listing 12.6 demonstrates how to fire the CheckEmailJob only once.
Listing 12.6. Using a Fire-Once Trigger for the CheckEmailJob
package org.cavaness.quartzbook.chapter12; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_12_6 { static Log logger = LogFactory.getLog(Listing_12_6.class); public static void main(String[] args) { Listing_12_6 example = new Listing_12_6(); example.runScheduler(); } public void runScheduler() { Scheduler scheduler = null; try { // Get a Scheduler instance from the Factory scheduler = StdSchedulerFactory.getDefaultScheduler(); // Start the scheduler scheduler.start(); // Create a JobDetail for the Job JobDetail jobDetail = new JobDetail("CheckEmailJob", Scheduler.DEFAULT_GROUP, CheckEmailJob.class); // Create a trigger that fires every 1 hour Trigger trigger = TriggerUtils.makeImmediateTrigger(0, 0); trigger.setName("emailJobTrigger"); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException ex) { // deal with any exceptions logger.error(ex); } } } |
Listing 12.6 uses the static makeImmediateTrigger() method on the TRiggerUtils clas and passes 0 for the repeatCount and 0 for the repeatInterval so that the trigger fires only once.
Replacing an Existing Scheduled Job
Quartz provides the flexibility to modify jobs that are already scheduled. It supports this by allowing the JobDetail to be replaced with a modified JobDetail. To show an example of this, let's update our CheckEmailJob class from Listing 12.4. The version that was shown in Listing 12.4 hard-coded the mail properties within the job class. It would be better if those properties were passed in so they could be changed at will; let's change the CheckEmailJob to allow for that. Listing 12.7 shows an updated version of that job.
Listing 12.7. An Updated CheckEmailJob That Allows Properties to Be Passed In
package org.cavaness.quartzbook.chapter12; import java.util.Properties; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.NoSuchProviderException; import javax.mail.Session; import javax.mail.Store; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class CheckEmailJob implements Job { public static String HOST_KEY = "mailHost" ; public static String USERNAME_KEY = "username"; public static String PASSWORD_KEY = "password"; String mailHost = " some.mail.host"; String username = "aUsername"; String password = "aPassword"; public CheckEmailJob() { super(); } public void execute(JobExecutionContext context) throws JobExecutionException { loadMailProperties(context.getJobDetail().getJobDataMap()); checkMail(); } protected void loadMailProperties(JobDataMap map) { if (map.getString(HOST_KEY) != null) { mailHost = map.getString(HOST_KEY); } if (map.getString(USERNAME_KEY) != null) { username = map.getString(USERNAME_KEY); } if (map.getString(PASSWORD_KEY) != null) { password = map.getString(PASSWORD_KEY); } } protected void checkMail() { // Get session Session session = null; try { // Get system properties Properties props = System.getProperties(); session = Session.getDefaultInstance(props, null); // Get the store Store store = session.getStore("pop3"); store.connect(mailHost, username, password); // Get folder Folder folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); // Get directory Message message[] = folder.getMessages(); int numOfMsgs = message.length; if (numOfMsgs > 0) { for (int i = 0, n = numOfMsgs; i < n; i++) { System.out.println("(" + i + " of " + numOfMsgs + "): " + message[i].getFrom()[0] + " " + message[i].getSubject()); } } else { System.out.println("No Messages for user"); } // Close connection folder.close(false); store.close(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { CheckEmailJob job = new CheckEmailJob(); job.checkMail(); } } |
The main difference between the CheckEmailJob from Listing 12.7 and the version in 12.4 is the loadMailProperties() method. This method is called when the job is first executed and checks the JobDataMap to see if the mail properties were set within the map. If so, those are used. If not, the defaults within the job class are used.
Listing 12.8 shows how the properties can be set up within the JobDataMap and passed to the job. This listing also shows how you can change the job by replacing the JobDetail with a modified instance.
Listing 12.8. An Example Showing How to Update a Scheduled Job
package org.cavaness.quartzbook.chapter12; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_12_8 { static Log logger = LogFactory.getLog(Listing_12_8.class); public static void main(String[] args) { Listing_12_8 example = new Listing_12_8(); example.runScheduler(); } public void runScheduler() { Scheduler scheduler = null; try { // Get a Scheduler instance from the Factory scheduler = StdSchedulerFactory.getDefaultScheduler(); // Start the scheduler scheduler.start(); // Create a JobDetail for the Job JobDetail jobDetail = new JobDetail("CheckEmailJob", Scheduler.DEFAULT_GROUP, CheckEmailJob.class); // Set the properties used by the job jobDetail.getJobDataMap().put(CheckEmailJob.HOST_KEY, "host1"); jobDetail.getJobDataMap().put(CheckEmailJob.USERNAME_KEY, "username"); jobDetail.getJobDataMap().put(CheckEmailJob.PASSWORD_KEY, "password"); // Create a trigger that fires at 11:30pm every day Trigger trigger = TriggerUtils.makeDailyTrigger(23, 30); trigger.setName("emailJobTrigger"); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); // Update the Job with a different mail host jobDetail.getJobDataMap().put(CheckEmailJob.HOST_KEY, "host2"); scheduler.addJob(jobDetail, true); } catch (SchedulerException ex) { // deal with any exceptions logger.error(ex); } } } |
The code in Listing 12.8 shows two things. First, it shows how you can pass mail properties to the job class through the JobDataMap. Second, it illustrates how you can use the addJob() method to update the JobDetail of an already scheduled job. The addJob() method takes a Boolean argument that tells the Scheduler whether to replace the scheduled JobDetail with the one being passed in. The job name and group must match the one within the Scheduler for it to be replaced with the new one. Typically, your code would retrieve the existing job, modify the contents of its JobDataMap, and then resave it.
Updating an Existing Trigger
You might also need to update an existing trigger for a job. You can replace a trigger with a different one as long as it's for the same job. You can replace a trigger by using the rescheduleJob() method on the Scheduler:
Trigger newTrigger = // Create a new Trigger // Replace the old trigger with a new one sched.rescheduleJob(jobName, Scheduler.DEFAULT_GROUP, newTrigger);
Listing Jobs in the Scheduler
If you were building a GUI for Quartz, you might need to list the jobs registered with the Scheduler. Listing 12.9 presents an approach for doing just that.
Listing 12.9. An Example of Listing the Jobs Within the Scheduler
package org.cavaness.quartzbook.chapter12; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_12_9 { static Log logger = LogFactory.getLog(Listing_12_9.class); public static void main(String[] args) { Listing_12_9 example = new Listing_12_9(); example.runScheduler(); } public void runScheduler() { Scheduler scheduler = null; try { // Get a Scheduler instance from the Factory scheduler = StdSchedulerFactory.getDefaultScheduler(); // Start the scheduler scheduler.start(); // Create a JobDetail for the Job JobDetail jobDetail = new JobDetail("CheckEmailJob", Scheduler.DEFAULT_GROUP, CheckEmailJob.class); // Create a trigger that fires at 11:30pm every day Trigger trigger = TriggerUtils.makeDailyTrigger(23, 30); trigger.setName("emailJobTrigger"); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); String[] jobGroups = scheduler.getJobGroupNames(); int numOfJobGroups = jobGroups.length; for (int i = 0; i < numOfJobGroups; i++) { System.out.println("Group: " + jobGroups[i] + " contains the following jobs"); String[] jobsInGroup = scheduler.getJobNames(jobGroups[i]); int numOfJobsInGroup = jobsInGroup.length; for (int j = 0; j < numOfJobsInGroup; j++) { System.out.println(" - " + jobsInGroup[j]); } } } catch (SchedulerException ex) { // deal with any exceptions logger.error(ex); } } } |
Listing 12.9 registers a single job, the CheckEmailJob from Listing 12.7, and then demonstrates how to loop through the JobGroups and list the jobs within each group. In a GUI, this list could be presented in a list box or a drop-down list.
Listing Triggers Within the Scheduler
You can also list the triggers in a manner similar to that in Listing 12.9 The code would look very similar, but with triggers instead.
String[] triggerGroups = sched.getTriggerGroupNames(); int numOfTriggerGroups = triggerGroups.length; for (i = 0; i < numOfTriggerGroups; i++) { System.out.println("Group: " + triggerGroups[i] + " contains the following triggers"); String{[] triggersInGroup = sched.getTriggerNames(triggerGroups[i]); int numOfTriggersInGroup = triggersInGroup.length; for (j = 0; j < numOfTriggersInGroup; j++) { System.out.println("- " + triggersInGroup[j]); } }
If you need to list the triggers of a single job, you can use the gettriggersOfJob() method found on the Scheduler. This method returns a TRigger[] of the triggers associated with the job.
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