Search code examples
javaspring-mvcjobsstatefulquartz

Quartz job can not be executed in Spring cron


The similar questions seem to be asked many times,but I still can't find the exact answer.I use org.quartz.StatefulJob to setup a scheduled job to retry it for 5 times by simulating an Exception and Spring cron to trigger it, but some exception occurred,here's the details:

@Service(value = "cronJobRetryImpl")
public class DuMiSynchronizationRetryRest implements StatefulJob {
    private Logger logger = Logger.getLogger(getClass());

    @Override
    public void execute(JobExecutionContext context) throws 
JobExecutionException {
        logger.info("enter execute ...");
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        long count = 
            NumberUtils.toLong(String.valueOf(dataMap.get("count")));

        if (count >= 5) {
            logger.info("Retries exceeded...");
            JobExecutionException e = new JobExecutionException("Retries 
                exceeded");
            e.setUnscheduleAllTriggers(true);
            throw e;
        }

        try {
            logger.info("business begin...");
            dataMap.put("count", 0);
            int n = 10 / 0;
        } catch (Exception e) {
            count++;
            dataMap.putAsString("count", count);
            JobExecutionException e2 = new JobExecutionException(e);
            try {
                logger.info("Thread begin to sleep for 3 seconds...");
                Thread.sleep(3000);
            } catch (InterruptedException e1) {
                logger.info("interrupted exception",e1);
            }
            e2.refireImmediately();
            logger.info("job refired ...");
            throw e2;
        }

    }
}

Here's the Spring xml:

<!-- retry test start -->
<bean id="cronJobRetryImplTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" lazy-init="true">
    <property name="targetObject">
        <ref bean="cronJobRetryImpl" />
    </property>
    <property name="targetMethod">
        <value>execute</value>
    </property>
    <property name="concurrent" value="false" />
</bean>
<bean id="cronJobRetryTime" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail">
        <ref bean="cronJobRetryImplTask" />
    </property>
    <property name="cronExpression">
        <value>20 30 9 * * ?</value>
    </property>
</bean>
<!-- retry test end -->
<bean id="startQuertz" lazy-init="false" autowire="no"     
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronJobRetryTime" />
        </list>
    </property>

I planned to trigger it on every 9:30:20AM,and the result:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cronJobRetryTime' defined in class path resource [applicationContext-job.xml]: Cannot resolve reference to bean 'cronJobRetryImplTask' while setting bean property 'jobDetail'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cronJobRetryImplTask' defined in class path resource [applicationContext-job.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodException: tv.huan.cms.services.rest.DuMiSynchronizationRetryRest.execute()
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:329)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:607)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:925)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:472)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:388)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:293)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5157)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5680)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1702)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1692)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cronJobRetryImplTask' defined in class path resource [applicationContext-job.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodException: tv.huan.cms.services.rest.DuMiSynchronizationRetryRest.execute()
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1455)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:323)
... 24 more
Caused by: java.lang.NoSuchMethodException: tv.huan.cms.services.rest.DuMiSynchronizationRetryRest.execute()
at java.lang.Class.getMethod(Unknown Source)
at org.springframework.util.MethodInvoker.prepare(MethodInvoker.java:178)
at org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean.afterPropertiesSet(MethodInvokingJobDetailFactoryBean.java:198)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
... 31 more

-----------------------first edit---------------------------

Yes,yes,samabcde's replay is effective and thanks samabcde for your patience and effort,but problems seem to be in the code itself.I try to run these codes but the job can not be executed again when the simulated exception occurred:

INFO  2018-11-02 14:43:00,022 [DuMiSynchronizationRetryRest.java,16] - enter 
execute ...
INFO  2018-11-02 14:43:00,027 [DuMiSynchronizationRetryRest.java,28] - 
business begin...
INFO  2018-11-02 14:43:00,027 [DuMiSynchronizationRetryRest.java,36] - 
Thread begin to sleep for 3 seconds...
INFO  2018-11-02 14:43:03,028 [DuMiSynchronizationRetryRest.java,42] - job 
refired ...
INFO  2018-11-02 14:43:03,031 [JobRunShell.java,221] - Job 
DEFAULT.cronJobRetryImplTask threw a JobExecutionException: 
org.quartz.JobExecutionException: java.lang.ArithmeticException: / by zero 
[See nested exception: java.lang.ArithmeticException: / by zero]
   at tv.huan.cms.services.rest.DuMiSynchronizationRetryRest.execute(DuMiSynchronizati onRetryRest.java:34)
   at org.quartz.core.JobRunShell.run(JobRunShell.java:216)
   at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
   Caused by: java.lang.ArithmeticException: / by zero
   at tv.huan.cms.services.rest.DuMiSynchronizationRetryRest.execute(DuMiSynchronizat
 ionRetryRest.java:30)
   ... 2 more

the log showed up an exception and no more clue.How can I achieve my goals?


Solution

  • Spring Quartz BeanCreationException problem

    From the Spring xml

    <bean id="cronJobRetryImplTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" lazy-init="true">
        <property name="targetObject">
            <ref bean="cronJobRetryImpl" />
        </property>
        <property name="targetMethod">
            <value>execute</value>
        </property>
        <property name="concurrent" value="false" />
    </bean>
    

    The arguments of MethodInvoker(MethodInvokingJobDetailFactoryBean extend) is not specified, hence it assumes there is no argument. As a result the exception

    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cronJobRetryImplTask' defined in class path resource [applicationContext-job.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodException: tv.huan.cms.services.rest.DuMiSynchronizationRetryRest.execute()

    is thrown as there is no method execute without arguments in DuMiSynchronizationRetryRest class.

    To resolve the problem, JobDetailFactoryBean should be used instead of MethodInvokingJobDetailFactoryBean as said in the Java Doc of MethodInvokingJobDetailFactoryBean

    Avoids the need for implementing a one-line Quartz Job that just invokes an existing service method on a Spring-managed target bean.

    However, DuMiSynchronizationRetryRest is implemented as a Quartz Job, and using execute method of Job, which requires JobExecutionContext. In this case, JobDetailFactoryBean should be the appropriate factory bean. To change the factory bean, just update the bean definition of cronJobRetryImplTask as

    <bean id="cronJobRetryImplTask"
        class="org.springframework.scheduling.quartz.JobDetailFactoryBean"
        lazy-init="true">
        <property name="jobClass">
            <value>gov.housingauthority.pass.app.DuMiSynchronizationRetryRest
            </value>
        </property>
    </bean>
    

    Since cronJobRetryImpl bean is not needed after the change the @Service annotation of can be removed also.

    Finally, since StatefulJob is deprecated, as suggested in the Java Doc of StatefulJob,

    use DisallowConcurrentExecution and/or PersistJobDataAfterExecution annotations instead.

    Job Not Retry Immediately problem

    For the problem that the job does not retry immediately, this is due to using e2.refireImmediately();, which is a getter only.To set the refire flag:

    For quartz on or before version 1.5.2, we can use new JobExecutionException(e, true) where the second parameter will set the refireImmediately as true.

    After version 1.7.2, setter method setRefireImmediately(boolean refireImmediately); is provided.