Home JBoss Server JBoss Quartz
12 | 03 | 2010
JBoss 5 AS Book
"JBoss AS 5 development" reviews
Please share your feedback/review with other readers!
Banner
Dashboard
Advertise with Us
Banner
RSS Feed
Login
Sign here for the NewsLetter.



Poll
What book could be in your wish list next XMas ?
 
JBoss admin resources
Banner
JBoss howto

How can you solve deployment errors caused by large war/jar/ear files ?

jboss recipe of the day ...
Read More

How do you configure your .war to be deployed after your EJB ?

jboss recipe of the day ...
Read More

How do I configure a Queue/Topic to work in a cluster?

JBoss recipe of the day ...
Read More
JBoss Quartz PDF Print E-mail
Written by F.Marchioni   
Friday, 17 October 2008 07:02

Many business processes require asynchronous job scheduling. The Java/J2EE community has produced several attempts at solving this issue: starting with Java 1.3, Sun added the java.util.Timer and java.util.TimerTask classes to help add basic Timer functionality to the core language. Although the Timer and TimerTask can work for simple requirements, an heavy duty job scheduling needs much more.  

Quartz is an open source project that offers an extensive set of job scheduling features. The two fundamental units of Quartz's scheduling package are jobs and triggers.  

jboss quartzA 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);

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.6.X.jar and quartz-jboss-1.6.X.jar (For this sample we're using Version 1.6.1 (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();   }    .....  } 

jboss quartzHint: 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 !

  




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

JBoss.org Search
Custom Search
Comments
Search
arjan   |2008-11-17 17:56:34
Very nice and well written article

I wonder about one thing though; your
example shows looking up the StdScheduler from JNDI and using it to schedule a
job from within a Servlet.

Would it be safe to do the same thing from within an
EJB bean? (e.g. from within a message driven bean, or stateless session bean)
admin   |2008-11-17 18:06:11
thank you for your feedback.
>Would it be safe to do the same thing >from
within an EJB bean?
yes, read it when your EJB is inited and cache the JNDI
reference in an EJB field.
In a future article I'd like to write something
about Clustering Quartz...stay tuned !
regards
Francesco
qin  - Some exceptions   |2008-11-20 17:38:14
Thanks for the article, nice and helpful.

I use quartz1.6.0 in Jboss
4.2.3 GA, and the quartz properties I set are:

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

 org.quartz.jobStore.driverDelegateClass =
 org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 
org.quartz.jobStore.dataSource = QUARTZ

 
org.quartz.jobStore.tablePrefix = qrtz_

 org.quartz.dataSource.QUARTZ.jndiURL = java:LocalQuartzDS


I will get the following exceptions:

java.lang.reflect.UndeclaredThrowableException
at $Proxy64.rollback(Unknow
n
Source)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.rollb ackConn
ection(JobStoreSupport.java:3503)
at org.quartz.impl.jdbcjobstore.JobStore
Support.execu teInNonManagedTXLock(JobStoreSupport.java:3669)
a t&nbs
p;org.quartz.impl.jdbcjobstore.JobStoreTX.executeInL ock(JobStoreTX.ja...
qin   |2008-11-20 17:59:39
Sorry, the previous post was too long, one sentence is missing: I wonder whether
someone has any idea about this exception. Thanks a lot in advance!
henk   |2008-11-21 00:19:14
Quote:
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.


By itself that sounds logical, but that doesn't really explain why Quartz
needs both of these. Since the JobStores are used internally by
Quartz, it seems to me that its Quartz that decides which one is going
to be used.

Is this situation dependent? I.e. if I add a trigger to
the scheduler within a transaction, will Quartz then use the TX based
DS and otherwise the non-TX one, or is this totally unrelated?
benoitx  - How about dependencies?     |2008-11-27 14:07:51
Hi

Excellent article, thanks! I have a comment on the lack of details regarding
managing dependencies.

Say your Quartz service uses the JobStoreCMT, it is
therefore dependent on the DataSource deployment. This can be done easily by
adding:
jboss.jcaervice=DataSourceBinding,name=A ppendiumDS
to the
quartz-service.xml

And that works fine. However, my Jobs depend on classes
that are defined in my EAR file. How could I specify that Quartz needs to wait
until the ear is deployed? In other words, Quartz should be the LAST thing to be
deployed?

It is so basic and yet... I cannot google anything about it...

Many
thanks

Benoit
Only registered users can write comments!

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

Last Updated ( Wednesday, 01 April 2009 20:20 )