Quartz and Spring

Maarten Winkels

Quartz is a Java Framework for scheduling. It allows applications to schedule tasks for execution in the future. Spring is a Java IoC container. It helps glue together the components that make up an application. This blog elaborates on where the two meet and how they can work together to make developing your application easier.

Parameterized Jobs
Quartz is build around the concepts of jobs and triggers. A job is a task that will be executed when an associated trigger fires. Often jobs are parameterized; they use certain input parameters that determine how the task is performed. Quartz uses a JobDataMap to contain key-value pairs that your job implementation can use. The map is passed into the org.quartz.Job.execute(JobExecutionContext) method (encapsulated in the JobExecutionContext) and your job has to know what keys to use to find the parameter values it uses.
Spring is used to setting values on JavaBeans based upon some configuration. To simplify the task of writing a Quartz job, Spring comes with an implementation of org.quartz.Job that does just that: It will use the key-value pairs in the JobDataMap to set JavaBean properties on itself. To leverage this functionality, you need to extend the org.springframework.scheduling.quartz.QuartzJobBean class, move your implementation of the execute(JobExecutionContext) method to the executeInternal(JobExecutionContext) method and make setters for all parameters that your job uses.
The example project that is attached to this blog, contains a PrinterJob, that is very simple.

public class PrinterJob extends QuartzJobBean {
    
    protected String message;

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println(message);
    }
    
    public void setMessage(String message) {
        this.message = message;
    }

}

This job just prints a message to the console. The message is a parameter to the job. Where does the job get its message from? It uses the JobDataMap, but it doesn’t do that directly, it uses the functionality in its super class to set its properties. To schedule a certain message for printing, the following fragment is used.

private void scheduleJob(String message, Class clazz, SimpleTrigger trigger) {
    JobDetail jobDetail = new JobDetail(message, "MSG", clazz);
    JobDataMap map = new JobDataMap();
    map.put("message", message);
    jobDetail.setJobDataMap(map);
    scheduler.scheduleJob(jobDetail, trigger);
}

The SimpleTrigger, JobDetail and JobDataMap are quartz infrastructure to do the actual scheduling. The scheduler is a org.quartz.Scheduler instance, which is quartz’ main component. The most important part here is the key that is used to store the message in the JobDataMap. This must be the same as the property name on your Job bean. In this case, since the setter method is named “setMessage”, the key to use is “message”. Your job implementation no longer has to worry about the job data map and can just use its properties. This is another example of “Inversion of Control”.

Services and Resources
In a J2EE environment your job often needs to use services or resources that are not easily serialized. Quartz can store non serializable objects in its JobDataMap, but only if the schedule is not persisted. If you need your schedule to be robust, you can use a persistent JobStore. To use such a JobStore all data in your JobDataMap needs to be serializable. To accommodate this, a Quartz Scheduler has a SchedulerContext which is another map that can contain services and resources as values.
Since you are using Spring, all your services and resources are defined in your ApplicationContext. Your Quartz Scheduler can also be configured in this context using org.springframework.scheduling.quartz.SchedulerFactoryBean. This factory bean has a convenience method to set up the SchedulerContext.


  
    
      
  



The above configuration adds the heavyService to the SchedulerContext. A job using this service could now get this service from the JobExecutionContext using the correct key. Springs QuartzJobBean implementation also searches the SchedulerContext for values when setting the properties of the Job bean, thus making your implementation so much easier.

public class ServicePrinterJob extends PrinterJob {
    
    private HeavyService heavyService;

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        heavyService.doPrint(message);
    }
    
    public void setHeavyService(HeavyService heavyService) {
        this.heavyService = heavyService;
    }
}

Now the heavyService property is fetched from the SchedulerContext and the message property is fetched from the JobDataMap. All of this happens outside the scope of your implementation.

Circular references
Quite often scheduling will introduce a circular reference between beans in your context. A service that is used to do some task, will in some cases schedule tasks for itself using a Quartz Scheduler. Spring can handle circular references as long as you use setter injection and have lazy beans. By default, the SchedularFactoryBean is not lazy, which will lead to a BeanCreationException when the application context is loaded. Setting the bean to initialize lazily resolves this problem. The following configuration will only load because of this property.


  
    
    
  





  



  

In this case, a method on the RecursiveService is called, which schedules a job that again uses the RecursiveService.

Conclusion

The Quartz-Spring integration makes writing Quartz jobs for a Spring application really easy. The code you need to write is very simple and concise and very easy to test. The spring configuration is not complex.

Comments (1)

  1. Balaji Loganathan - Reply

    September 17, 2007 at 11:50 am

    Very useful blog Maarten.

Add a Comment