-
Spring, Quartz and Auto-wiring of Quartz jobs
Posted on January 16th, 2008 9 commentsFacing Null Pointer Exceptions in Quartz jobs? This is an example on how to do Spring Quartz Integration with Auto-wiring.
Quartz Enterprise scheduler is a neat application that lets you setup periodic jobs within a J2EE or J2SE environment. Quartz is not as powerful as other commercial enterprise job schedulers like Autosys but its ability to run simple cron-like jobs within an Application server environment is commendable. Above all Quartz and Spring integrates very nicely with a little effort.
I have seen that many people complain about Null Pointer Exception in the Quartz jobs. It is due to the inability of Quartz to auto-wire Spring beans since the job bean is instantiated by Quartz. One option people talk about is to use the MethodInvokingJobDetailFactoryBean of Spring to invoke a method in your spring bean. The disadvantage is that you would not have access to the JobExcecutionContext which I think is important in many advanced jobs. Here is a document that explains this method:
http://www.zabada.com/technology/Wiki.jsp?page=SpringAndQuartzI have successfully instantiated Job beans will full auto-wiring using the technique explained below. In this technique I pass the actual job bean name to a delegating job bean through the job data map. Now the delegating job bean instantiates the actual job bean based on name retrieved from the job data map. After instantiating the job bean, the delegating job bean invokes methods in it as defined by the contract, JobInterface.
First you define an interface (or a contract) for your jobs. Let us call it JobInterface:
// JobInterface.java package com.harinair.jobs; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public interface JobInterface { public void init(JobExecutionContext jobExecutionContext) throws JobExecutionException public void execute() throws JobExecutionException; public void destroy(); }Now write the job bean that implements this interface:
package com.harinair.jobs; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; /** * @author hari.gangadharan * @spring.bean id="checkMailJobBean" autowire="byName" */ public class CheckMailJobBean implements JobInterface { public void init (JobExecutionContext jobExecutionContext) throws JobExecutionException { // Do your job initialization } public void execute() throws JobExecutionException { // Write your job logic } public void destroy() { // Do the cleanup } // Add all others setters and getters for your // Spring auto-wiring }It is time to write a delegating job bean that can execute the above job bean:
package com.harinair.jobs; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.SchedulerContext; import org.quartz.SchedulerException; import org.springframework.context.ApplicationContext; import org.springframework.scheduling.quartz.QuartzJobBean; /** * @author hari.gangadharan */ public class DelegatingJobBean extends QuartzJobBean { private static final String APPLICATION_CONTEXT_KEY = "applicationContext"; private static final String JOB_BEAN_NAME_KEY = "job.bean.name"; protected Log log = LogFactory.getLog(getClass()); @Override protected final void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { SchedulerContext schedulerContext = null; try { schedulerContext = jobExecutionContext.getScheduler() .getContext(); } catch(SchedulerException e) { throw new JobExecutionException( "Failure accessing scheduler context", e); } ApplicationContext appContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY); String jobBeanName = (String) jobExecutionContext .getJobDetail() .getJobDataMap().get(JOB_BEAN_NAME_KEY); log.info("Starting job: " + jobBeanName); JobInterface jobBean = (JobInterface) appContext .getBean(jobBeanName); try { jobBean.init(jobExecutionContext); jobBean.execute(); } finally { jobBean.destroy(); } } }Now you are ready to make the application context changes:
Spring Context (I have also attached it here):
http://shared-files.s3.amazonaws.com/springContext.xml< !DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">applicationContext Now fire up your application. Your job will run daily at 6:30 AM.
9 responses to “Spring, Quartz and Auto-wiring of Quartz jobs”
-
The best information i have found exactly here. Keep going Thank you
-
[...] Finally, i know that the spring context and quartz was run as a different singleton context and start at the same time. That is why my service class always have a null value. Ok, now everything is clear for me. What i have to do now is injecting the service rather than made it auto wiring. (Auto wiring is also possible, you can read it at harinair blog) [...]
-
Rizwan May 3rd, 2010 at 11:34
How you have deployed this application?
-
Andrew August 12th, 2010 at 06:40
Thank you for the great solution!
-
hooohh…
luks lik one has to have around 20+ lines of xml config for every job (scheduled at different timing). You article has “Autowire” in the title but did not really depict it properly and did not make sense reading it. Please point out a place where you are doing autowire in this example.
If auto-wiring is working, why do you have to use line no. 09 as follows
-
[ref bean="serviceLocator"]
-
and why do you have to get the bean explicitly….
String jobBeanName = (String) jobExecutionContext
.getJobDetail()
.getJobDataMap().get(JOB_BEAN_NAME_KEY);
log.info(”Starting job: ” + jobBeanName); -
[...] the service rather than made it auto wiring. (Auto wiring is also possible, you can read it at harinair blog) Ok, now let’s start to create a [...]
-
Hari Gangadharan May 9th, 2011 at 21:05
@Naga: This blog was written 3 years back – at that time Annotations for example @Autowired was not there. The problem I was trying to solve was that the Quartz job was initialized by Quartz and not by Spring. Hence the Quartz job bean will have nulls for all Spring injected fields. Now since Autowiring is there there may be better ways – I have not researched on that recently.
Leave a reply
-

