Quartz advanced tutorial

quartz tutorialThis is an advanced quartz tutorial which has been tested on JBoss AS 6 which ships with Quartz 1.8.3 release. Here we will show some advanced features of the Quartz scheduler.

Introduction:

If you want to read an introduction to Quartz and JBoss read this tutorial which shows the basics notions and provides some basic samples.

Installing Quartz on JBoss AS 6

The first good news is that if you are using JBoss AS 6 you don’t need to download anything to get working with Quartz.
On earlier releases of the application server (in particular with the 4.x series) it was suggested to remove the Quartz scheduler
from the installation and download a fresh Quartz installation.

# How to store parameters in your Jobs

One common requirement for a Job is to store some properties in it, so that you can customize your Job. The JobDataMap is what you need for it:

JobDetail job = new JobDetail("job2", "group1", MyJob.class);
 
JobDataMap map = new JobDataMap();
 
map.put("param1","some data");
map.put("param2",new Long(50));
job.setJobDataMap(map);
CronTrigger  trigger = new CronTrigger("trigger2", "group1", "job2", "group1",
 "15 0/2 * * * ?");
scheduler.addJob(job, true); 

Then, in your Job class, you can retrieve the parameters simply with:

public class MyJob implements Job{
 public void execute(JobExecutionContext context) throws JobExecutionException
 {
 JobDataMap map = context.getMergedJobDataMap();

 String s =  map.getString("param1");
 long l = map.getLong("param2");
 }
}

Why this is very useful ? as a matter of fact you can use JobDataMap to create dynamically new instances of the same Job which can be parametrized by the content of the JobDataMap.

# How to start/stop the Quartz Scheduler automatically from an Enterprise Application ?

Many developers are not aware that Quartz has many utility classes to manage automatically the start and stop of the Scheduler. You don’t need to re-invent the wheel. The QuartzInitializerServlet does that exactly the job of starting and stopping the Scheduler for you:

Place in your web.xml the following snippet:

<servlet>
 <servlet-name>QuartzInitializer</servlet-name>
 <servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
 <init-param>
 <param-name>shutdown-on-unload</param-name>
 <param-value>true</param-value>
 </init-param>
 <init-param> 
 <param-name>start-scheduler-on-load</param-name> 
 <param-value>true</param-value> 

 </init-param>
 <load-on-startup>1</load-on-startup>
</servlet>

This will automatically start the scheduler (equivalent of scheduler.start()) when the Web application is deployed and shut it down when the application is undeployed.

# How to load the quartz property file from a Web application ?

 

The simplest way to have your Quartz properties file loaded from a Web application is using again the QuartzInitializerServlet and the “config-file” parameter:

 <servlet>
 <servlet-name>QuartzInitializer</servlet-name>
 <servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
 <init-param>
 <param-name>shutdown-on-unload</param-name>
 <param-value>true</param-value>
 </init-param>
 <init-param>

 <param-name>config-file</param-name>

 <param-value>quartz.properties</param-value>

 </init-param>
 <load-on-startup>2</load-on-startup>
 </servlet>
 

And here’s a sample quartz.properties file that will be loaded:

org.quartz.scheduler.instanceName = QuartzScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 8
org.quartz.threadPool.threadPriority = 8
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

 

Now place the quartz.properties file in the WEB-INF folder of your project and it will be picked up automatically by the QuartzInitializerServlet.

quartz advanced tutorial jboss

Note: if you don’t want to use the QuartzInitializer features you can still set to false the start-scheduler-on-load and shutdown-on-unload properties.


# How do you place a listener on all your Jobs ?

The JobListener interface contains a set of methods that are invoked when certain key events occur in the life cycle of a job. This is can be helpful to keep track of what your Jobs are doing.
By using the addGlobalListener method of the scheduler, you can place a global listener on all jobs:

JobDetail job = new JobDetail("job2", "group1", MyJob.class);
 
JobListener jobListener =
 new MyJobListener();
scheduler.addGlobalJobListener(jobListener);

CronTrigger  trigger = new CronTrigger("trigger2", "group1", "job2", "group1","15 0/2 * * * ?");
scheduler.addJob(job, true);

And here’s the corresponding interface which needs to implement the JobListener interface:

package sample;

import org.quartz.JobExecutionContext;

public class MyJobListener implements JobListener {

 public String getName() {
 return getClass().getSimpleName();
 }

 public void jobExecutionVetoed(JobExecutionContext context) {
 String jobName = context.getJobDetail().getName();
 System.out.println(jobName + " is vetoed to be executed");

 }

 public void jobToBeExecuted(JobExecutionContext context) {
 String jobName = context.getJobDetail().getName();
 System.out.println(jobName + " is going to be executed");

 }

 public void jobWasExecuted(JobExecutionContext context,
 JobExecutionException exc) {
 String jobName = context.getJobDetail().getName();
 System.out.println(jobName + " was executed");

 }

}

STICKY NOTE: By using global listeners you can count the amount of jobs which are been executed or are going to be executed. This is quite important if you are going to monitor your Quartz application

# How do you set a listener on a specific Job ?

 

 

Sometimes you need a fine grained control over your Jobs, thus you can set a listener JUST on a specific Job. You don’t
need to change too much the earlier code- just use the Scheduler’s addJobListener method instead and register the listener BOTH on the Scheduler and on the Job:

JobDetail job = new JobDetail("job2", "group1", MyJob.class);
CronTrigger  trigger = new CronTrigger("trigger2", "group1", "job2", "group1","15 0/2 * * * ?");
JobListener jobListener =
 new MyJobListener();

scheduler.addJobListener(jobListener);

// Listener set on JobDetail before scheduleJob()
jobDetail.addJobListener(jobListener.getName());

// Register the JobDetail and Trigger
scheduler.scheduleJob(job, trigger);

#How do you configure Jobs using an XML file ?

 

If you want to configure your Jobs declaratively, Quartz can do that for you! Here’s how to configure a Cron Job without writing a line of code!
Place at first this file jobs.xml (you can call it as you like it) at the root of your project. (It needs to be moved in the WEB-INF/classes of your Web application)


<job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd"
 version="1.8">

 <schedule>
 <job>
 <name>AutomaticJob</name>
 <group>MYJOB_GROUP</group>
 <description>The job description</description>
 <job-class>sample.MyJob</job-class>

 </job>

 <trigger>
 <cron>
 <name>my-trigger</name>
 <group>MYTRIGGER_GROUP</group>
 <job-name>AutomaticJob</job-name>
 <job-group>MYJOB_GROUP</job-group>
 <cron-expression>15 0/2 * * * ?</cron-expression>

 </cron>
 </trigger>
 </schedule>
</job-scheduling-data> 

Here we are firing an AutomaticJob bound the the class sample.MyJob which will fire every 2 minutes. In order to activate this Job we need setting the jobInitializer class which is responsible for firing jobs automatically. Add to your quartz.properties the following properties and that’s all!

org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames =/jobs.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
org.quartz.plugin.jobInitializer.scanInterval = 10
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false

I’ve noticed that the Quartz’s File scan keeps printing the message “Job file not found”. That’s a bit odd, because I’ve tested Jobs are fired correctly!