paul
paul

Reputation: 65

got nullPointerException when use quartz in clustered system,but when remove the database config ,it works fine

this is my quartz config:

@Bean
public SchedulerFactoryBean startQuartz(DataSource dataSource, PlatformTransactionManager annotationDrivenTransactionManager,
                                        CronTriggerFactoryBean remoteProjectTrigger, ApplicationContext applicationContext) throws IOException {
    SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    schedulerFactoryBean.setDataSource(dataSource);
    schedulerFactoryBean.setTransactionManager(annotationDrivenTransactionManager);
    schedulerFactoryBean.setOverwriteExistingJobs(false);
    schedulerFactoryBean.setQuartzProperties(quartzProperties());
    schedulerFactoryBean.setTriggers(remoteProjectTrigger.getObject());
    schedulerFactoryBean.setApplicationContext(applicationContext);
    return schedulerFactoryBean;
}

@Bean(name = "detailFactoryBean")
public MethodInvokingJobDetailFactoryBean detailFactoryBean(){
    MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean ();
    bean.setTargetBeanName("quartzRemoteProject");
    bean.setTargetMethod ("run");
    bean.setConcurrent (false);
    return bean;
}


@Bean
public CronTriggerFactoryBean remoteProjectTrigger(MethodInvokingJobDetailFactoryBean detailFactoryBean){
    CronTriggerFactoryBean trigger = new CronTriggerFactoryBean ();
    trigger.setJobDetail (detailFactoryBean.getObject ());
    trigger.setCronExpression ("0/5 * * ? * *");
    return trigger;
}


@Bean("quartzRemoteProject")
public QuartzRemoteProject quartzRemoteProject(){
    return new QuartzRemoteProject();
}


public Properties quartzProperties() throws IOException {
    Properties prop = new Properties();
    prop.put("quartz.scheduler.instanceName", "ServerScheduler");
    prop.put("org.quartz.scheduler.instanceId", "AUTO");
    prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
    prop.put("org.quartz.scheduler.instanceId", "CLUSTERED");
    prop.put("org.quartz.scheduler.jobFactory.class", "org.quartz.simpl.SimpleJobFactory");
    prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
    prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
    prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
    prop.put("org.quartz.jobStore.isClustered", "true");
    prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
    prop.put("org.quartz.threadPool.threadCount", "5");

    return prop;
}

this config will got an exception

org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.2.3.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.2.3.jar:na]
Caused by: java.lang.NullPointerException: null
at org.springframework.scheduling.quartz.JobMethodInvocationFailedException.<init>(JobMethodInvocationFailedException.java:40) ~[spring-context-support-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean$MethodInvokingJob.executeInternal(MethodInvokingJobDetailFactoryBean.java:271) ~[spring-context-support-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75) ~[spring-context-support-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.2.3.jar:na]
... 1 common frames omitted

i find the problem is in MethodInvokingJobDetailFactoryBean.class's inner class MethodInvokingJob

context.setResult(this.methodInvoker.invoke());

the methodInvoker is null;

when remove the schedulerFactoryBean.setDataSource(dataSource); schedulerFactoryBean.setTransactionManager(annotationDrivenTransactionManager); schedulerFactoryBean.setQuartzProperties(quartzProperties());

this config will work. any one can hlep:)

Upvotes: 0

Views: 1155

Answers (1)

paul
paul

Reputation: 65

it's because class MethodInvokingJobDetailFactoryBean cannot be serialized to database, spring-context-support did not provide the serialized version,It is suppose throw an SerializationException instead of NullPointerException,i don't figure out this. but only solve this this problem,I find two ways.

1、overwrite MethodInvokingJobDetailFactoryBean serializable version,you can find a sample in https://jira.spring.io/browse/SPR-3797 ,but for the latest quartz version(2.2.3),you may do some update work.then change the config MethodInvokingJobDetailFactoryBean to BeanInvokingJobDetailFactoryBean like follow

@Bean(name = "jobDetailFactoryBean")
public BeanInvokingJobDetailFactoryBean detailFactoryBean(){
    BeanInvokingJobDetailFactoryBean bean = new BeanInvokingJobDetailFactoryBean ();
    bean.setTargetBean("quartzRemoteProject");
    bean.setTargetMethod ("run");
    bean.setDurable(true);
    bean.setShouldRecover(true);
    bean.setConcurrent (false);
    return bean;
}

2、write yourself QuartzJobBean, like this

@Log4j
@NoArgsConstructor
public class MyDetailQuartzJobBean extends QuartzJobBean {

private String targetObject;
private String targetMethod;
private ApplicationContext ctx;

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    try {
        Object bean = ctx.getBean(targetObject);
        Method m = bean.getClass().getMethod(targetMethod);
        m.invoke(bean, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void setApplicationContext(ApplicationContext applicationContext) {
    this.ctx = applicationContext;
}

public void setTargetObject(String targetObject) {
    this.targetObject = targetObject;
}

public void setTargetMethod(String targetMethod) {
    this.targetMethod = targetMethod;
}
}

I use lombok annotation. then use this class replace the MethodInvokingJobDetailFactoryBean,config like this

@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
    JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
    jobDetailFactoryBean.setJobClass(MyDetailQuartzJobBean.class);
    Map map=new HashMap<>();
    map.put("targetObject","quartzRemoteProject");
    map.put("targetMethod","run");
    jobDetailFactoryBean.setJobDataAsMap(map);
    jobDetailFactoryBean.setDurability(true);
    return jobDeta
}

ps:the second solution you must add quartz-jobs otherwise cannot find JobDetailFactoryBean

<dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz-jobs</artifactId>
        <version>2.2.3</version>
    </dependency>

Upvotes: 0

Related Questions