Affects: \
Hello, @jhoeller tagging you since you've contributed majorly to the quartz implementation I was able to figure out a method to make the MethodInvokingJobDetailFactoryBean compatible with the persistent store. Note that CronUtil is a utility class that I've made, and assume the invoked methods/constants are able to do the job. The gist is to simply replace the jobDataMap "methodInvoker" with primitives, and use those strings to create instances in the create instance method. The example doesn't take into consideration arguments(feature can at least enable methods that don't require arguments), but just provides a possible way of achieving this. Would love to contribute as a generic implementation with a PR. Here's how:
// Build JobDetail instance.
JobDetailImpl jdi = new JobDetailImpl();
jdi.setName(name);
jdi.setGroup(this.group);
jdi.setJobClass((Class) jobClass);
jdi.setDurability(true);
// remove this line
// jdi.getJobDataMap().put("methodInvoker", this);
// add method/class qualifying primitives to the jobDataMap
jobDetail.getJobDataMap().put(CronUtil.METHOD_INVOKING_JOB_TARGET_METHOD, "targetMethodAsString");
jobDetail.getJobDataMap().put(CronUtil.METHOD_INVOKING_JOB_TARGET_CLASS, "targetClassAsString");
// and some others in order to support static methods, Maybe a serializable POJO.
And I overrode the createInstance method,
/**
* Class that overrides the createJobInstance method so as to
* delegate job instance creation to the beanFactory,
* rather than have quartz do it by calling {@code clazz.getInstance()}.
* This allows for spring-based autowiring and classes with
* members in their constructors
*/
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) {
JobDetail jobDetail = bundle.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
if (jobDataMap.getBoolean(CronUtil.IS_METHOD_INVOKING_JOB)) {
return getMethodInvokingJob(jobDetail, jobDataMap);
}
return getNormalJob(jobDetail);
}
private Object getNormalJob(JobDetail jobDetail) {
Class<? extends Job> jobClass = jobDetail.getJobClass();
return beanFactory.createBean(jobClass, CronUtil.resolveAutowiringType(jobClass), true);
}
private MethodInvokingJobDetailFactoryBean.MethodInvokingJob getMethodInvokingJob(JobDetail jobDetail, JobDataMap jobDataMap) {
Object instance = CronUtil.getInstance(jobDataMap.getString(CronUtil.METHOD_INVOKING_JOB_TARGET_CLASS));
String method = jobDataMap.getString(CronUtil.METHOD_INVOKING_JOB_TARGET_METHOD);
MethodInvoker methodInvoker = getMethodInvoker(instance, method);
Class<? extends Job> jobClass = jobDetail.getJobClass();
MethodInvokingJobDetailFactoryBean.MethodInvokingJob bean;
bean = getResolvedMethodInvokingJob(jobDetail, jobClass);
bean.setMethodInvoker(methodInvoker);
return bean;
}
private MethodInvokingJobDetailFactoryBean.MethodInvokingJob getResolvedMethodInvokingJob(JobDetail jobDetail, Class<? extends Job> jobClass) {
MethodInvokingJobDetailFactoryBean.MethodInvokingJob bean;
if (jobDetail.isConcurrentExectionDisallowed()) {
bean = (MethodInvokingJobDetailFactoryBean.StatefulMethodInvokingJob)
beanFactory.createBean(jobClass, CronUtil.resolveAutowiringType(jobClass), true);
} else {
bean = (MethodInvokingJobDetailFactoryBean.MethodInvokingJob)
beanFactory.createBean(jobClass, CronUtil.resolveAutowiringType(jobClass), true);
}
return bean;
}
private MethodInvoker getMethodInvoker(Object targetObject, String targetMethod) {
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetObject(targetObject);
methodInvoker.setTargetMethod(targetMethod);
try {
methodInvoker.prepare();
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new CronException(e);
}
return methodInvoker;
}
}