JBoss Quartz tutorial

Update: Please notice that an updated tutorial about Quartz Scheduler has been published on the following article: Quartz 2 tutorial on JBoss AS 7

Quartz is a full-featured, open source job scheduling service that can be integrated with, or used along side virtually any Java EE or Java SE application - from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs;

 The two fundamental units of Quartz's scheduling package are jobs and triggers.  

A job is an executable task that can be scheduled, while a trigger provides a schedule for a job

You can make Java component executable by the scheduler simply by making it implement the Job interface.   

package sample; 
public class TestJob implements org.quartz.Job {
 public TestJob() {  }
 public void execute(org.quartz.JobExecutionContext jobExecutionContext)
 throws org.quartz.JobExecutionException {
 System.out.println("Job executed!");
 }
} 

There are two basic kinds of Triggers: SimpleTrigger and CronTrigger.

SimpleTrigger provides basically the same functionality you get from the Timer API. It should be used if the Job should be triggered once, followed possibly by repeats at a specific interval. You can specify start date, end date, repeat count, and repeat interval for this kind of trigger.

With this description, you may not find it surprising to find that the properties of a SimpleTrigger include: a start-time, and end-time, a repeat count, and a repeat interval. All of these properties are exactly what you'd expect them to be, with only a couple special notes related to the end-time property.

Here's an example of SimpleTrigger: 

long startTime = System.currentTimeMillis() + 5000L;

SimpleTrigger trigger = new SimpleTrigger("myTrigger",
 null,
 new Date(startTime),
 null,
 0,
 0L);

This will create a trigger that fires now and repeats every 10 seconds.

If you want to repeat the trigger zero or more times you can use the repeat parameter which can be set to SimpleTrigger.REPEAT_INDEFINITELY for triggers that will fire non-stop.

SimpleTrigger trigger = new SimpleTrigger("myTrigger",
 null,
 new Date(),
 null,
 SimpleTrigger.REPEAT_INDEFINITELY,
 10L * 1000L);

A simpler way to create a Trigger is by means of the org.quartz.TriggerUtils class which allows to define a trigger using a simpler method notation.

For example if you were to create a trigger that fires every 10 seconds you could use simply:

JobDetail job = new JobDetail("job1", "group1", TestJob.class);

Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);
 scheduler.scheduleJob(job, trigger);

CronTrigger is another type of trigger which provides a better flexibility to schedule jobs on a more realistic basis. CronTriggers allow us to express schedules such as "every weekday at 7:00 p.m." or "every five minutes on Saturday and Sunday."

Here's an example of Cron trigger:
This creates a trigger that fires at 10:30, 11:30, 12:30, and 13:30, on every Wednesday and Friday.

 CronTrigger trigger  new CronTrigger(
 "Income Report",
 "Report Generation"
 );
 trigger.setCronExpression(
 "0 30 10-13 ? * WED,FRI" 
 );

Ok, now that we have some basic concepts about quartz we need to learn how to use this framework. Basically you can run Quartz in two modes:


 1) Standalone Quartz server:

This way you are responsible of starting/stopping your own Quartz scheduler. This is the simpler way and could be used for smaller projects :   

package sample; 
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzServer {
 public static void main(String[] args) {
 QuartzServer server = new QuartzServer();
 try {
 server.startScheduler();
 } catch (SchedulerException ex) {
 ex.printStackTrace();
 }
 }
 protected void startScheduler() throws SchedulerException {
 SchedulerFactory sf=new StdSchedulerFactory();
 Scheduler sched=sf.getScheduler();
 sched.start();
 JobDetail jd=new JobDetail("myjob",sched.DEFAULT_GROUP,TestJob.class);
 SimpleTrigger st=new SimpleTrigger("mytrigger",sched.DEFAULT_GROUP,new Date(),
 null,SimpleTrigger.REPEAT_INDEFINITELY,60L*1000L);
 sched.scheduleJob(jd, st);
 
 }
}
                                         

Here the 'default' scheduler -defined in "quartz.properties"- is retreived from the SchedulerFactory and a Job is enlisted to fire every 60 seconds. (The TestJob class is exposed at the beginning of this article) 

2) Embed Quartz inside an application server

Larger projects may as well use Quartz embedded inside the container. This leads to a lot of advanced features like job clustering which we'll explore in the next articles.
JBoss comes already with some Quartz libraries. Anyway I suggest you to download the Quartz libraries from the home page:

http://www.opensymphony.com/quartz/download.action
Let's clean the house!

If you want to patch correctly Quartz, at first remove the Quartz libraries under server/lib and replace them with quartz-1.8.X.jar and quartz-jboss-1.8.X.jar (For this sample we're using Version 1.8.4 (latest stable release). Remove as well "quartz-ra.rar" in the "deploy" folder.

Add quartz service to jboss

In order to register Quartz Service on JBoss you need to register its Mbean. In the Quartz distribution you can find a couple of samples of quartz-service.xml. Here's a simple one, we'll discuss more in depth about it later...

<?xml version="1.0" encoding="UTF-8"?> <server> 
 <mbean code="org.quartz.ee.jmx.jboss.QuartzService" 
 name="user:service=QuartzService,name=QuartzService">    
 <attribute name="JndiName">Quartz</attribute> 
 <attribute name="Properties">        
 org.quartz.scheduler.instanceName = DefaultQuartzScheduler 
 org.quartz.scheduler.rmi.export = false 
 org.quartz.scheduler.rmi.proxy = false 
 org.quartz.scheduler.xaTransacted = false 
 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool  
 org.quartz.threadPool.threadCount = 5 
 org.quartz.threadPool.threadPriority = 4 
 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore  
 </attribute>     
 </mbean> 
</server>  

Ok so now we're ready to rock!

This is a sample Servlet doGet method which looks up the Scheduler registered in the JNDI tree and schedules a new Job that simply fires every 5 minutes.

 public void doGet(HttpServletRequest request, HttpServletResponse response)

 throws ServletException, IOException {
 try {
 InitialContext ctx = new InitialContext();
 StdScheduler scheduler = (StdScheduler) ctx.lookup("Quartz");

 JobDetail jd=new JobDetail("myjob",scheduler.DEFAULT_GROUP,TestJob.class);
 CronTrigger ct = new CronTrigger("cronTrigger","group2",""0 0/5 * * * ?");
 scheduler.scheduleJob(jd,ct);

 } catch (Exception exc) {  
 exc.printStackTrace();
 }

 .....
 } 

Hint: have you got a class cast exception when you lookup the StdScheduler ? I guess you have added Quartz libraries under WEB-INF/lib : remove them from there !

 


How to reschedule a Quartz Job ?

Supposing that you have scheduled a Job with a GUI interface and you want to allow the user to change the scheduling plan of this job. Here we are first defining a Job which is triggered to start every 30 seconds. After a pause, the Job is rescheduled to start every 10 seconds:

JobDetail job = new JobDetail("job1", "group1", TestJob.class);

Trigger trigger = TriggerUtils.makeSecondlyTrigger(30);
trigger.setName("trigger1");
trigger.setGroup("group1");
trigger.setJobName("job1");
trigger.setJobGroup("group1");

Date ft = scheduler.scheduleJob(job, trigger);

System.out.println("Job will run at "+ft

Thread.sleep(10000);

trigger = TriggerUtils.makeSecondlyTrigger(10);
trigger.setName("trigger1");
trigger.setGroup("group1");
trigger.setJobName("job1");
trigger.setJobGroup("group1");

 

ft = scheduler.rescheduleJob("trigger1", "group1", trigger);

System.out.println("Job rescheduled to run at: " + ft);
 
out.println("Job rescheduled");

Where Quartz stores Jobs ?

I guess you noticed the property:

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

What does it mean ? the Quartz framework provides two basic types of JobStores. The first type, which utilizes ordinary memory (RAM) to persist Scheduler information, is called RAMJobStore. This type of JobStore is the easiest to configure and get up and running. For many applications, this JobStore will be sufficient. However, since the Scheduler information is stored in the memory assigned to the JVM, when the application is stopped, all knowledge of the schedule is lost. If you need to persist schedule information between restarts, then you're going to need the second type of JobStore.

The second type of JobStore actually comes with two different implementations in the framework, but both are commonly referred to as JDBC JobStores. Both JDBC JobStores use a JDBC driver and require a backing from a relational database to persist the scheduler information. The two types differ in whether or not you want to control the database transactions or relinquish control to an application container such as JBoss. (This is similar to the difference between Bean Managed Transactions or Container Managed Transactions from the J2EE world.

In order to work with JDBC Stores, the first thing to do is to create the required tables on the DB (you can find the scripts under doc/DbTables ), then choose the appropriate Jdbc Store in quartz-service.xml

This is an example for JobStoreCMT:


org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass =  org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = QUARTZ
org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.dataSource.QUARTZ.jndiURL = java:/jdbc/QuartzDS
org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:/jdbc/QuartzNoTxDS


References:
http://www.opensymphony.com/quartz/wikidocs/Tutorial.html

 

Follow us on Twitter