3

I managed to configure and schedule a Quartz job using JobStoreTX persistent store in Spring Boot ( version 4.2.5 ). Here is how I schedule the job. First :

public class MyJob implements Job{
    @Autowired
    IService service;

    @Override
      public void execute(JobExecutionContext context) throws JobExecutionException {
        service.doSomething();
      }
}

@Autowired seems like it wont work in a Quartz job implementation because it wont be instantiated by Spring. Hence, im facing the famous JavaNullPointerException.

Second, in order to get hold of Spring-managed beans in a Quartz job, I used org.springframework.scheduling.quartz.SchedulerFactoryBean to manage the Quartz lifecycle :

public class MyJob implements Job{

    @Override
      public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            ApplicationContext applicationContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
            IService service= applicationContext.getBean(IService.class);
            service.getManualMaxConfig();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
      }
}

And then :

<bean id="scheduler"
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="applicationContextSchedulerContextKey" value="applicationContext" />

</bean>

The sad news is that im also facing JavaNPE. I also try these suggestions, in vain ..

LINK

Whats wrong with what im doing?

Update 1 : Before trying to inject service, i tried to pass some Params as @ritesh.garg suggests.

public class MyJob implements Job{

    private String someParam;
    private int someParam2;

    public void setSomeParam(String someParam) {
        this.someParam = someParam;
    }


    public void setSomeParam2(int someParam2) {
        this.someParam2 = someParam2;
    }

    @Override
      public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("My job is running with "+someParam+' '+someParam2);
      }
}

And my jobBean.xml looks like :

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">

 <bean id="scheduler"
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="applicationContextSchedulerContextKey" value="applicationContext" />

</bean> 


<bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.quartz.service.MyJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="someParam" value="some value"/>
            <entry key="someParam2" value="1"/>
        </map>
    </property>
</bean>

</beans>

I dont know why, but the parameters arent passed and it prints :

My job is running with null 0

Ps : I imported the jobBean.xml into Application.java . So i dont know what am i missing ?

Update 2 : Here is my detailed code :

@Component
public class JobScheduler{
    Timer timer = new Timer();    
   @PostConstruct
    public void distributeAutomaticConf(){
        try {
            timer.schedule(new ServiceImpl(), 10000);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }       
    }  
}

Service Impl :

@Transactional
@Component
public class ServiceImpl extends TimerTask implements IService{

@Override
    public void run() {
         final SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = null;

            try {
              scheduler = factory.getScheduler();


                  final JobDetailImpl jobDetail = new JobDetailImpl();
                  jobDetail.setName("My job executed only once.. ");
                  jobDetail.setJobClass(MyJob.class);

              SimpleTrigger trigger = (SimpleTrigger) newTrigger()
                        .withIdentity("trigger_", "group_")
                        .build(); 
              scheduler.start();
              scheduler.scheduleJob(jobDetail, trigger);

              System.in.read();
              if (scheduler != null) {
                scheduler.shutdown();
              }
            } catch (final SchedulerException e) {
              e.printStackTrace();
            } catch (final IOException e) {
              e.printStackTrace();
            }
    }
}

MyJob :

public class MyJob extends QuartzJobBean{

    @Autowired
    IService service;
    @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {         SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);    
        service.doSomething();  
    }
}

jobBean.xml :

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">

 <bean id="scheduler"
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="applicationContextSchedulerContextKey" value="applicationContext" />

</bean> 


<bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.quartz.service.MyJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="someParam" value="some value"/>
            <entry key="someParam2" value="1"/>
        </map>
    </property>
</bean>

</beans>

quartz.properties :

org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.dataSource.myDS.driver = org.postgresql.Driver
org.quartz.dataSource.myDS.URL = jdbc:postgresql://localhost:5432/myDB
org.quartz.dataSource.myDS.user = admin
org.quartz.dataSource.myDS.password = admin
org.quartz.dataSource.myDS.maxConnections = 10

org.quartz.scheduler.skipUpdateCheck=true

console :

java.lang.NullPointerException: null
    at com.quartz.service.MyJob.executeInternal(MyJob.java:27) ~[classes/:na]
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:113) ~[spring-context-support-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.2.1.jar:na]
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.2.1.jar:na]

2016-06-05 11:35:16.839 ERROR 25452 --- [eduler_Worker-1] org.quartz.core.ErrorLogger              : Job (DEFAULT.My job executed only once..  threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception.
    at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.2.1.jar:na]
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.2.1.jar:na]
Caused by: java.lang.NullPointerException: null
    at com.quartz.service.MyJob.executeInternal(MyJob.java:27) ~[classes/:na]
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:113) ~[spring-context-support-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.2.1.jar:na]
    ... 1 common frames omitted
12
  • you need to use extends QuartzJobBean instead of implements Job. Also see my answer here for a better way to inject the dependencies, IMO.
    – yishaiz
    Commented Jun 5, 2016 at 9:50
  • After remplacing implements Job with extends QuartzJobBean and adding SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); it still prints null and 0 ..
    – Daniel
    Commented Jun 5, 2016 at 10:17
  • can you post your updated code? Also I want to make sure, there is only one NPE bug which is for the IService service in the Job?
    – yishaiz
    Commented Jun 5, 2016 at 10:36
  • Also I prefer to define those beans in the App itself, e.g. here, along with @Bean public SchedulerFactoryBean schedulerFactoryBean() { final SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setDataSource(dataSource); bean.setQuartzProperties(quartzProperties()); return bean; }, but this may not be related to your bug itself.
    – yishaiz
    Commented Jun 5, 2016 at 10:39
  • your updated code doesn't include @Autowired IService service; nor SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); nor service.doSomething(); in the MyJob.java code
    – yishaiz
    Commented Jun 5, 2016 at 11:09

4 Answers 4

4

I have experienced the same problem in past. My understanding on this issue is that beans instantiated in spring context cannot be injected in quartz context simply by using @Autowired annotation.

I managed to solve it by using setter based dependency injection. But the same is mentioned in the "LINK" you have added in the original post.

Pasting the relevant information from the link:

Update: Replaced implements Job with extends QuartzJobBean

public class MyJob extends QuartzJobBean {
    private String someParam;
    private int someParam2;

    public void setSomeParam(String someParam) {
        this.someParam = someParam;
    }

    public void setSomeParam2(String someParam2) {
        this.someParam2 = someParam2;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("My job is running with "+someParam+' '+someParam2);
    }
}

Here, someParam and someParam2 are being injected via setter dependency injection. Now the other part that makes this complete is to pass someParam and someParam2 in jobDataAsMap

<bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.my.MyJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="someParam" value="some value"/>
            <entry key="someParam2" value="1"/>
        </map>
    </property>
</bean>

In your case, it would be a value-ref="IserviceBeanId", instead of 'value' in entry. I would be surprised as well as curious, if this did not/does not work for you.

10
  • This could be useful to you as well: stackoverflow.com/questions/6990767/… Commented Jun 4, 2016 at 0:14
  • Can you try again after replacing implements Job with extends QuartzJobBean? I remember that I was using QuartzJobBean along with a cron trigger (type of trigger should not be related to the injection issue). To see a working example for reference, you can check this: mkyong.com/spring/spring-quartz-scheduler-example Commented Jun 5, 2016 at 1:42
  • The dependency injection via setters finally works ! Now i add value-ref="service" in entry to use the service method but it throws NPE. Here is the project : github.com/soufianeid/quartzSpring
    – Daniel
    Commented Jun 6, 2016 at 19:31
  • Yea, the code setup in this project seems correct. You need to name your setter correctly for IService It should be public void setService(IService service) since 'service' is the key in jobBean.xml. Also you will need to add the bean declaration for Iservice implementation in jobBean.xml and use the id of that bean as a value-ref Commented Jun 6, 2016 at 21:04
  • 2
    Here is a simple implementation with non-primitive type. I think you are over-complicating your setup. github.com/ritesh-garg/QuartzExample.git Commented Jun 9, 2016 at 2:24
2

I fix my problem implementing "InitializingBean" in my job;

    public class MyJob extends QuartzJobBean implements InitializingBean {

        private String someParam;
        private int someParam2;

        public void setSomeParam(String someParam) {
            this.someParam = someParam;
        }

        public void setSomeParam2(String someParam2) {
            this.someParam2 = someParam2;
        }

        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("My job is running with "+someParam+' '+someParam2);
        }
        @Override
        public void afterPropertiesSet() throws Exception {
        }
    }
2

The correct way from the most of the examples I've seen is to make your Job interface implementation a @Component

@Component
public class MyJob implements Job{ 
     @Autowired IService service; 
     @Override 
     public void execute(JobExecutionContext context) throws JobExecutionException{ 
          service.doSomething();
     } 
}
1
  • When using Spring Boot, it works like a charm. Commented Jun 15, 2021 at 14:21
0

We can use JobDataMap to pass the objects.

example: here restTemplate is Autowired.

JobDataMap newJobDataMap = new JobDataMap();
newJobDataMap.put("restTemplate", restTemplate);

JobDetail someJobDetail = JobBuilder
.newJob(QuartzJob.class)
.withIdentity(jobName, GROUP)
.usingJobData(newJobDataMap)
.build();

Not the answer you're looking for? Browse other questions tagged or ask your own question.