Quartz advanced tutorial

This is an advanced quartz tutorial which has written for Quartz 1.8.3 release. Here we will show some advanced features of the Quartz scheduler.

Please note: If you want an updated tutorial about Quartz 2 with WildFly, we recommend checking this article: Quartz 2 tutorial on JBoss EAP and WildFly

How to store parameters in 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:

Firstly, place in your web.xml the following snippet:

<servlet>
    <servlet-name>
        QuartzInitializer
    </servlet-name>
    <display-name>
        Quartz Initializer Servlet
    </display-name>
    <servlet-class>
        org.quartz.ee.servlet.QuartzInitializerServlet
    </servlet-class>
    <load-on-startup>
        1
    </load-on-startup>
    <init-param>
        <param-name>config-file</param-name>
        <param-value>/some/path/my_quartz.properties</param-value>
    </init-param>
    <init-param>
        <param-name>shutdown-on-unload</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>wait-on-shutdown</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>
</servlet>

To manage Quartz start/stop we have included the QuartzInitializerServlet to load-on-startup. 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.

Next, please note that you can also configure some init parameters for this Servlet:

The parameter ‘config-file‘ allows to specify the path (and filename) of your Quartz properties file. See the next section for more details.

The parameter ‘shutdown-on-unload‘ allows to specify whether you want scheduler.shutdown() as soon as the servlet is unloaded (usually when the application server is being shutdown). Possible values are “true” or “false”. The default is “true”.

The parameter ‘wait-on-shutdown‘ has effect when ‘shutdown-on-unload’ is set “true”, and indicates whether you want scheduler.shutdown(true) called when the listener is unloaded. Possible values are “true” or “false”. The default is “false”.

The parameter ‘start-scheduler-on-load‘ allows to specify whether you want the scheduler.start() method called when the servlet is first loaded. If set to false, your application will need to call the start() method before the scheduler begins to run and process jobs. Possible values are “true” or “false”. The default is “true”, which means the scheduler is started.

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>

Then, 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

Next, 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 to 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");

 }

}

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 to 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 to 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