Monday, July 18, 2011

'External' Quartz on JBoss 5.1

On our current project, we have the requirement to be able to run our application on more than JBoss AS alone. When confronted with the need to schedule certain tasks, we considered a few options:
  • Use EJB timers: Due to the nature of the tasks (e.g. interdependencies) this mechanism is not suitable - it's simply just not sophisticated enough.
  • Use the Quartz functionality provided along with the JBoss distribution: While this may do exactly what we need, it wouldn't be portable to app servers from other vendors.
So the decision was made to use Quartz, not as readily available from JBoss but as an add-on library ('external', if you will). I found a couple of articles that pointed me in the right direction:
We used Quartz version 2.0.2 - the latest and greatest at the time I write this - and since Maven is our build tool of choice, the following dependency pulls all required libs into our project:

  org.quartz-scheduler
  quartz-jboss
  2.0.2
  provided
 
We use provided scope here since we won't be including Quartz in our project's deliverables; instead we put such dependencies on our server explicitly. Either approach would work, though.

1) Clean up your installation.

So, the first job at hand is to remove the Quartz artifacts from our JBoss installation. I guess it makes sense to prevent different versions from showing up in your classpath. The files to be removed are:
  • ${jboss.home.dir}/common/lib/quartz.jar
  • ${jboss.home.dir}/server/[SERVER_NAME]/deploy/quartz-ra.rar (in EAP this is an exploded RAR, so remove the directory)
Before deleting anything from the common/lib directory, be sure that there aren't any other servers running from the same AS installation that need that file!

2) Add the required Quartz libraries to you server.

The following file should be in place after this step:
  • ${jboss.home.dir}/server/[SERVER_NAME]/lib/quartz-all-2.0.2.jar
That's right. Just one jar, very convenient - and the added bonus is that this already contains the stuff that's needed for deployment in other app servers, so no need to have different dependencies in different deployments.

Note that this file is not downloaded if you include the dependencies through Maven as indicated above, you have to download the full distribution to get it.

3) Add an MBean to the JBoss configuration.

The following is a simple example of a Quartz MBean configuration:

  
   Quartz
   
    org.quartz.scheduler.instanceName = DefaultQuartzScheduler
    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
   
  
 
If you use this, no extra DataSource needs to be configured and Quartz keeps its jobs in memory. For more specifics on the configuration possibilities (there are a lot!), see http://www.quartz-scheduler.org/docs/configuration/.

Put the XML above in a x-service.xml file in your server's deploy dir, like e.g.:
  • ${jboss.home.dir}/server/[SERVER_NAME]/deploy/quartz-service.xml
4) Point Quartz at the jobs you want done.

Obviously it now is possible to use Quartz from code inside deployed applications. Just retrieve the scheduler from JNDI like so:
InitialContext ctx = new InitialContext();
 Scheduler scheduler = (Scheduler) ctx.lookup("Quartz");
But for our purpose this just isn't good enough, we want to be able to schedule tasks from configuration files. To accomplish that, we need to perform a number of steps:

a) Enable the Quartz plugin that reads jobs and triggers from an indicated XML file. This is done by adding the following properties to the configuration shown in step 3):
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
   org.quartz.plugin.jobInitializer.fileNames = ${jboss.server.home.dir}/conf/quartz-jobs.xml
   org.quartz.plugin.jobInitializer.scanInterval = 120
For details on this plugin see http://www.quartz-scheduler.org/api/2.0.0/org/quartz/plugins/xml/XMLSchedulingDataProcessorPlugin.html.

b) Create a class that implements the org.quartz.Job interface for each such a task. This interface exposes exactly one method, void execute(JobExecutionContext ctx), which is called when the job is triggered.

c) Provide the quartz-jobs.xml file that is indicated in the extra configuration in step a) (the file name and path can be adjusted to your liking) with the appropriate timing to start your jobs. For our tasks we use Cron-like jobs, with the Quartz CronTrigger (see http://www.quartz-scheduler.org/api/2.0.0/org/quartz/CronTrigger.html). An example configuration is:

  
  *  
    * 
  
    true
    false 
  
  
  
   
    TestJob
    com.acme.quartz.TestJob
   

   
    
     TestCronTrigger
     TestJob
     0 0 3 ? * MON-FRI
    
   
      

 
In this example the task executed by the com.acme.quartz.TestJob class is triggered at 3:00 AM on weekdays.

One last CAVEAT: On Windows, the ${jboss.server.home.dir} expression resolves to a String that contains backward slashes ('\') instead of forward slashes as path delimiter. The way the Quartz extension for JBoss reads in the properties is not able to cope with that, so you may need to provide a full path explicitly for any file names.

6 comments:

  1. getting a classnotfound exception for job class when i start the jboss 5.0.1 can you help in this regard

    ReplyDelete
    Replies
    1. Make sure that you put the Quartz jar file in a correct 'lib' directory, i.e. one that is configured for the specific server you're running. These are configured in the ${jboss.home.dir}/server/[SERVER_NAME]/conf/jboss-service.xml file.

      In the default case, there are the following entries:

      <classpath codebase="${jboss.server.lib.url}" archives="*"/>
      <classpath codebase="${jboss.common.lib.url}" archives="*"/>

      Step 2) assumes that at least the first entry is there; if it's not, you need to either adjust your configuration or put the jar file in a directory that is configured instead.

      Hope this helps!

      Delete
    2. Were you able to resolve the classnotfound exception for job class. I'm in the same boat. It would greatly helpful to share what you did for this? Thanks.

      Delete
  2. a good article, it helped me a lot

    ReplyDelete
  3. hi Maurice, thanks for the useful post. Have you tried to make it work on JBOSS 7 so far? do you know how can I configure quartz as a Jboss service with the new architecture?
    thanks

    ReplyDelete
  4. Good article.

    I have 1 comment in your last statement: if file quartz-jobs.xml is in default/conf directory, you may use its name instead of full path (i.e. org.quartz.plugin.jobInitializer.fileNames = quartz-jobs.xml ) is fine.

    ReplyDelete