34

i am using quartz with spring and i want to inject/use another class in the job class and i don't know how to do it correctly

the xml:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

   <!-- Scheduler task -->
   <bean name="schedulerTask" class="com.mkyong.quartz.SchedulerTask" />

   <!-- Scheduler job -->
   <bean name="schedulerJob"
       class="org.springframework.scheduling.quartz.JobDetailBean">

     <property name="jobClass" value="com.mkyong.quartz.SchedulerJob" />

     <property name="jobDataAsMap">
        <map>
          <entry key="schedulerTask" value-ref="schedulerTask" />
         </map>
      </property>
   </bean>

   <!-- Cron Trigger -->
   <bean id="cronTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerBean">

    <property name="jobDetail" ref="schedulerJob" />
    <property name="cronExpression" value="0/10 * * * * ?" />

   </bean>

   <!-- Scheduler -->
   <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="jobDetails">
       <list>
          <ref bean="schedulerJob" />
       </list>
    </property>

    <property name="triggers">
        <list>
        <ref bean="cronTrigger" />
        </list>
    </property>
   </bean>

</beans>

the quartz job:

package com.mkyong.quartz;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class SchedulerJob extends QuartzJobBean
{
    private SchedulerTask schedulerTask;

    public void setSchedulerTask(SchedulerTask schedulerTask) {
        this.schedulerTask = schedulerTask;
    }

    protected void executeInternal(JobExecutionContext context)
    throws JobExecutionException {

        schedulerTask.printSchedulerMessage();

    }
}

the task to be executed:

package com.mkyong.quartz;

public class SchedulerTask {

   public void printSchedulerMessage() {

       System.out.println("Struts 2 + Spring + Quartz ......");

   }
}

i want to inject another DTO class that deals with Database in the task class to do some database work in the task, how to do that ?

STW
  • 44,917
  • 17
  • 105
  • 161
Mahmoud Saleh
  • 33,303
  • 119
  • 337
  • 498
  • 1
    Link below has solved the solution for me http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#17394905 – pathfinder Sep 14 '14 at 09:18

5 Answers5

16

In your solution you are using the spring @Autowired annotation in a class that is not instantiated by Spring. Your solution will still work if you remove the @Autowired annotation because Quartz is setting the property, not Spring.

Quartz will try to set every key within the JobDataMap as a property. E.g. since you have a key "myDao" Quartz will look for a method called "setMyDao" and pass the key's value into that method.

If you want Spring to inject spring beans into your jobs, create a SpringBeanJobFactory and set this into your SchedulerFactoryBean with the jobFactory property within your spring context.

SpringBeanJobFactory javadoc:

Applies scheduler context, job data map and trigger data map entries as bean property values

Kieren Dixon
  • 947
  • 10
  • 9
  • 8
    For those interesed, you can find a `SpringBeanJobFactory` implementation to autowire automatically Quartz jobs in this answer (includes complete example): http://stackoverflow.com/a/15211030/787375 – jelies Mar 09 '13 at 07:41
11

Not sure if this is what you want, but you can pass some configuration values to the Quartz job. I believe in your case you could take advantage of the jobDataAsMap property you already set up, e.g.:

 <property name="jobDataAsMap">
    <map>
      <entry key="schedulerTask" value-ref="schedulerTask" />
      <entry key="param1" value="com.custom.package.ClassName"/>
     </map>
  </property>

Then you should be able to access it in your actual Java code in manual way:

protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    schedulerTask.printSchedulerMessage();
    System.out.println(context.getJobDetail().getJobDataMap().getString("param1"));
}

Or using the magic Spring approach - have the param1 property defined with getter/setter. You could try defining it with java.lang.Class type then and have the done automatically (Spring would do it for you):

 private Class<?> param1;

 // getter & setter

 protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    schedulerTask.printSchedulerMessage();
    System.out.println("Class injected" + getParam1().getName());
 }     

I haven't tested it though.

Grzegorz Oledzki
  • 23,614
  • 16
  • 68
  • 106
  • i want to be able to use a DTO object, and a domain object in the quartz job, and the autowiring doesn't work here, so can you tell me what to do then ? – Mahmoud Saleh Nov 24 '10 at 08:22
  • 2
    In your question you wrote you wanted to use the class, not an object. Anyway, what's the difference then? – Grzegorz Oledzki Nov 24 '10 at 09:24
  • In Quartz manual it says (as best practice): "Only store primitive data types in JobDataMap to avoid data serialization issues short and long-term". Does this solution really work well? Thanks. – aloplop85 Sep 08 '13 at 17:10
9
ApplicationContext springContext = 
    WebApplicationContextUtils.getWebApplicationContext(
        ContextLoaderListener.getCurrentWebApplicationContext().getServletContext()
    );
Bean bean = (Bean) springContext.getBean("beanName");
bean.method();
Tomik
  • 23,857
  • 8
  • 121
  • 100
Damian
  • 151
  • 2
  • 1
3

As mentioned in inject bean reference into a Quartz job in Spring? you can use spring SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

@Named
public class SampleJob implements Job {

    @Inject
    private AService aService;

   @Override
    public void execute(JobExecutionContext context)
        throws JobExecutionException {

       //Do injection with spring
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        aService.doIt();
       }
}

As mentioned it may not wotk on some spring version but I have tested it on 4.2.1.RELEASE which worked fine.

Community
  • 1
  • 1
Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173
0

this is my solution:

    public class MySpringBeanJobFactory extends
        org.springframework.scheduling.quartz.SpringBeanJobFactory implements
        ApplicationContextAware {
      private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.ctx = applicationContext;
    }


    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle)
            throws Exception {

        Object jobInstance = super.createJobInstance(bundle);
        ctx.getAutowireCapableBeanFactory().autowireBean(jobInstance);
        return jobInstance;
    }
}

then config the class of MySpringBeanJobFactory in the xml:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory">
            <bean class="com.xxxx.MySpringBeanJobFactory" />
    </property>
        <property name="configLocation" value="classpath:quartz.properties" />
        <property name="triggers">
            <list>
                <ref bean="cronTrigger"/>
            </list>
        </property>
    </bean>

Good luck ! :)

martin
  • 1
  • 1