diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java index c060093f974..caddf3584c1 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java @@ -16,17 +16,21 @@ package org.springframework.scheduling.quartz; +import java.lang.reflect.Method; + import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; +import org.springframework.util.ReflectionUtils; + /** * JobFactory implementation that supports {@link java.lang.Runnable} * objects as well as standard Quartz {@link org.quartz.Job} instances. * - *

Compatible with Quartz 1.x as well as Quartz 2.0, as of Spring 3.1. + *

Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1. * * @author Juergen Hoeller * @since 2.0 @@ -65,7 +69,12 @@ public class AdaptableJobFactory implements JobFactory { * @throws Exception if job instantiation failed */ protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { - return bundle.getJobDetail().getJobClass().newInstance(); + // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... + Method getJobDetail = bundle.getClass().getMethod("getJobDetail"); + Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle); + Method getJobClass = jobDetail.getClass().getMethod("getJobClass"); + Class jobClass = (Class) ReflectionUtils.invokeMethod(getJobClass, jobDetail); + return jobClass.newInstance(); } /** diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java index ee99f4fc803..dc3b9dffc2b 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,10 +43,9 @@ import org.springframework.util.Assert; * to automatically register a trigger for the corresponding JobDetail, * instead of registering the JobDetail separately. * - *

NOTE: This convenience subclass does not work with trigger - * persistence in Quartz 1.6, due to a change in Quartz's trigger handling. - * Use Quartz 1.5 if you rely on trigger persistence based on this class, - * or the standard Quartz {@link org.quartz.CronTrigger} class instead. + *

NOTE: This convenience subclass does not work against Quartz 2.0. + * Use Quartz 2.0's native CronTriggerImpl class or the new + * Quartz 2.0 builder API instead. * * @author Juergen Hoeller * @since 18.02.2004 diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java index eafd818a960..8ff97295ed4 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,10 @@ import org.springframework.context.ApplicationContextAware; * sensible defaults. This class uses the Spring bean name as job name, * and the Quartz default group ("DEFAULT") as job group if not specified. * + *

NOTE: This convenience subclass does not work against Quartz 2.0. + * Use Quartz 2.0's native JobDetailImpl class or the new + * Quartz 2.0 builder API instead. + * * @author Juergen Hoeller * @since 18.02.2004 * @see #setName diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java index 25ab176ad5b..bd0c5b6f68c 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,16 @@ import java.lang.reflect.InvocationTargetException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.Scheduler; import org.quartz.StatefulJob; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -61,6 +65,8 @@ import org.springframework.util.MethodInvoker; * You need to implement your own Quartz Job as a thin wrapper for each case * where you want a persistent job to delegate to a specific service method. * + *

Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1. + * * @author Juergen Hoeller * @author Alef Arendsen * @since 18.02.2004 @@ -70,7 +76,19 @@ import org.springframework.util.MethodInvoker; * @see #setConcurrent */ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethodInvoker - implements FactoryBean, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean { + implements FactoryBean, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean { + + private static Class jobDetailImplClass; + + static { + try { + jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl"); + } + catch (ClassNotFoundException ex) { + jobDetailImplClass = null; + } + } + private String name; @@ -174,14 +192,31 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class); // Build JobDetail instance. - this.jobDetail = new JobDetail(name, this.group, jobClass); - this.jobDetail.getJobDataMap().put("methodInvoker", this); - this.jobDetail.setVolatility(true); - this.jobDetail.setDurability(true); + if (jobDetailImplClass != null) { + // Using Quartz 2.0 JobDetailImpl class... + Object jobDetail = BeanUtils.instantiate(jobDetailImplClass); + BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(jobDetail); + bw.setPropertyValue("name", name); + bw.setPropertyValue("group", this.group); + bw.setPropertyValue("jobClass", jobClass); + bw.setPropertyValue("durability", true); + ((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this); + } + else { + // Using Quartz 1.x JobDetail class... + this.jobDetail = new JobDetail(name, this.group, jobClass); + this.jobDetail.setVolatility(true); + this.jobDetail.setDurability(true); + this.jobDetail.getJobDataMap().put("methodInvoker", this); + } // Register job listener names. if (this.jobListenerNames != null) { for (String jobListenerName : this.jobListenerNames) { + if (jobDetailImplClass != null) { + throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " + + "manually register a Matcher against the Quartz ListenerManager instead"); + } this.jobDetail.addJobListener(jobListenerName); } } @@ -225,12 +260,12 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod } - public Object getObject() { + public JobDetail getObject() { return this.jobDetail; } - public Class getObjectType() { - return JobDetail.class; + public Class getObjectType() { + return (this.jobDetail != null ? this.jobDetail.getClass() : JobDetail.class); } public boolean isSingleton() { diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java index 07ac232ea15..d10468aaf95 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java @@ -42,6 +42,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.util.ReflectionUtils; /** * Common base class for accessing a Quartz Scheduler, i.e. for registering jobs, @@ -50,13 +51,30 @@ import org.springframework.transaction.support.DefaultTransactionDefinition; *

For concrete usage, check out the {@link SchedulerFactoryBean} and * {@link SchedulerAccessorBean} classes. * + *

Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1. + * * @author Juergen Hoeller * @since 2.5.6 */ public abstract class SchedulerAccessor implements ResourceLoaderAware { - protected final Log logger = LogFactory.getLog(getClass()); + private static Class jobKeyClass; + private static Class triggerKeyClass; + + static { + try { + jobKeyClass = Class.forName("org.quartz.JobKey"); + triggerKeyClass = Class.forName("org.quartz.TriggerKey"); + } + catch (ClassNotFoundException ex) { + jobKeyClass = null; + triggerKeyClass = null; + } + } + + + protected final Log logger = LogFactory.getLog(getClass()); private boolean overwriteExistingJobs = false; @@ -68,7 +86,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { private List triggers; - private SchedulerListener[] schedulerListeners; private JobListener[] globalJobListeners; @@ -79,7 +96,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { private TriggerListener[] triggerListeners; - private PlatformTransactionManager transactionManager; protected ResourceLoader resourceLoader; @@ -322,8 +338,7 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { * @see #setOverwriteExistingJobs */ private boolean addJobToScheduler(JobDetail jobDetail) throws SchedulerException { - if (this.overwriteExistingJobs || - getScheduler().getJobDetail(jobDetail.getName(), jobDetail.getGroup()) == null) { + if (this.overwriteExistingJobs || !jobDetailExists(jobDetail)) { getScheduler().addJob(jobDetail, true); return true; } @@ -341,7 +356,7 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { * @see #setOverwriteExistingJobs */ private boolean addTriggerToScheduler(Trigger trigger) throws SchedulerException { - boolean triggerExists = (getScheduler().getTrigger(trigger.getName(), trigger.getGroup()) != null); + boolean triggerExists = triggerExists(trigger); if (!triggerExists || this.overwriteExistingJobs) { // Check if the Trigger is aware of an associated JobDetail. if (trigger instanceof JobDetailAwareTrigger) { @@ -361,12 +376,12 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { ex.getMessage() + " - can safely be ignored"); } if (this.overwriteExistingJobs) { - getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger); + rescheduleJob(trigger); } } } else { - getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger); + rescheduleJob(trigger); } return true; } @@ -376,34 +391,116 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { } + // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... + private boolean jobDetailExists(JobDetail jobDetail) throws SchedulerException { + if (jobKeyClass != null) { + try { + Method getJobDetail = Scheduler.class.getMethod("getJobDetail", jobKeyClass); + Object key = ReflectionUtils.invokeMethod(JobDetail.class.getMethod("getKey"), jobDetail); + return (ReflectionUtils.invokeMethod(getJobDetail, getScheduler(), key) != null); + } + catch (NoSuchMethodException ex) { + throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex); + } + } + else { + return (getScheduler().getJobDetail(jobDetail.getName(), jobDetail.getGroup()) != null); + } + } + + // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... + private boolean triggerExists(Trigger trigger) throws SchedulerException { + if (triggerKeyClass != null) { + try { + Method getTrigger = Scheduler.class.getMethod("getTrigger", triggerKeyClass); + Object key = ReflectionUtils.invokeMethod(Trigger.class.getMethod("getKey"), trigger); + return (ReflectionUtils.invokeMethod(getTrigger, getScheduler(), key) != null); + } + catch (NoSuchMethodException ex) { + throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex); + } + } + else { + return (getScheduler().getTrigger(trigger.getName(), trigger.getGroup()) != null); + } + } + + // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... + private void rescheduleJob(Trigger trigger) throws SchedulerException { + if (triggerKeyClass != null) { + try { + Method rescheduleJob = Scheduler.class.getMethod("rescheduleJob", triggerKeyClass, Trigger.class); + Object key = ReflectionUtils.invokeMethod(Trigger.class.getMethod("getKey"), trigger); + ReflectionUtils.invokeMethod(rescheduleJob, getScheduler(), key, trigger); + } + catch (NoSuchMethodException ex) { + throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex); + } + } + else { + getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger); + } + } + + /** * Register all specified listeners with the Scheduler. */ protected void registerListeners() throws SchedulerException { - if (this.schedulerListeners != null) { - for (SchedulerListener listener : this.schedulerListeners) { - getScheduler().addSchedulerListener(listener); + Object target; + boolean quartz2; + try { + Method getListenerManager = Scheduler.class.getMethod("getListenerManager"); + target = ReflectionUtils.invokeMethod(getListenerManager, getScheduler()); + quartz2 = true; + } + catch (NoSuchMethodException ex) { + target = getScheduler(); + quartz2 = false; + } + + try { + if (this.schedulerListeners != null) { + Method addSchedulerListener = target.getClass().getMethod("addSchedulerListener", SchedulerListener.class); + for (SchedulerListener listener : this.schedulerListeners) { + ReflectionUtils.invokeMethod(addSchedulerListener, target, listener); + } + } + if (this.globalJobListeners != null) { + Method addJobListener = target.getClass().getMethod( + (quartz2 ? "addJobListener" : "addGlobalJobListener"), JobListener.class); + for (JobListener listener : this.globalJobListeners) { + ReflectionUtils.invokeMethod(addJobListener, target, listener); + } + } + if (this.jobListeners != null) { + for (JobListener listener : this.jobListeners) { + if (quartz2) { + throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " + + "manually register a Matcher against the Quartz ListenerManager instead"); + } + getScheduler().addJobListener(listener); + } + } + if (this.globalTriggerListeners != null) { + Method addTriggerListener = target.getClass().getMethod( + (quartz2 ? "addTriggerListener" : "addGlobalTriggerListener"), TriggerListener.class); + for (TriggerListener listener : this.globalTriggerListeners) { + ReflectionUtils.invokeMethod(addTriggerListener, target, listener); + } + } + if (this.triggerListeners != null) { + for (TriggerListener listener : this.triggerListeners) { + if (quartz2) { + throw new IllegalStateException("Non-global TriggerListeners not supported on Quartz 2 - " + + "manually register a Matcher against the Quartz ListenerManager instead"); + } + getScheduler().addTriggerListener(listener); + } } } - if (this.globalJobListeners != null) { - for (JobListener listener : this.globalJobListeners) { - getScheduler().addGlobalJobListener(listener); - } - } - if (this.jobListeners != null) { - for (JobListener listener : this.jobListeners) { - getScheduler().addJobListener(listener); - } - } - if (this.globalTriggerListeners != null) { - for (TriggerListener listener : this.globalTriggerListeners) { - getScheduler().addGlobalTriggerListener(listener); - } - } - if (this.triggerListeners != null) { - for (TriggerListener listener : this.triggerListeners) { - getScheduler().addTriggerListener(listener); - } + catch (NoSuchMethodException ex) { + throw new IllegalStateException("Expected Quartz API not present: " + ex); } } diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java index ec6f762dac6..44d7f18ca7c 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,8 @@ import org.springframework.beans.factory.ListableBeanFactory; * Spring bean-style class for accessing a Quartz Scheduler, i.e. for registering jobs, * triggers and listeners on a given {@link org.quartz.Scheduler} instance. * + *

Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1. + * * @author Juergen Hoeller * @since 2.5.6 * @see #setScheduler diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java index 2abaab665dc..5dde075eda9 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java @@ -74,9 +74,7 @@ import org.springframework.util.CollectionUtils; * automatically apply to Scheduler operations performed within those scopes. * Alternatively, you may add transactional advice for the Scheduler itself. * - *

Note: This version of Spring's SchedulerFactoryBean supports Quartz 1.x, - * more specifically Quartz 1.5 or higher. The "jobSchedulingDataLocation" feature - * requires Quartz 1.6.1 or higher (as of Spring 2.5.5). + *

Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1. * * @author Juergen Hoeller * @since 18.02.2004 diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java index e50acf889ce..acda87ddb9e 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,10 +42,9 @@ import org.springframework.core.Constants; * to automatically register a trigger for the corresponding JobDetail, * instead of registering the JobDetail separately. * - *

NOTE: This convenience subclass does not work with trigger - * persistence in Quartz 1.6, due to a change in Quartz's trigger handling. - * Use Quartz 1.5 if you rely on trigger persistence based on this class, - * or the standard Quartz {@link org.quartz.SimpleTrigger} class instead. + *

NOTE: This convenience subclass does not work against Quartz 2.0. + * Use Quartz 2.0's native SimpleTriggerImpl class or the new + * Quartz 2.0 builder API instead. * * @author Juergen Hoeller * @since 18.02.2004 diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java index e03ff263011..dfcd158d9f9 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,16 @@ package org.springframework.scheduling.quartz; +import java.lang.reflect.Method; + +import org.quartz.JobDataMap; import org.quartz.SchedulerContext; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.BeanWrapper; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyAccessorFactory; +import org.springframework.util.ReflectionUtils; /** * Subclass of {@link AdaptableJobFactory} that also supports Spring-style @@ -33,6 +37,8 @@ import org.springframework.beans.PropertyAccessorFactory; * as bean property values. If no matching bean property is found, the entry * is by default simply ignored. This is analogous to QuartzJobBean's behavior. * + *

Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1. + * * @author Juergen Hoeller * @since 2.0 * @see SchedulerFactoryBean#setJobFactory @@ -68,15 +74,15 @@ public class SpringBeanJobFactory extends AdaptableJobFactory implements Schedul */ @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { - Object job = bundle.getJobDetail().getJobClass().newInstance(); + Object job = super.createJobInstance(bundle); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); if (isEligibleForPropertyPopulation(bw.getWrappedInstance())) { MutablePropertyValues pvs = new MutablePropertyValues(); if (this.schedulerContext != null) { pvs.addPropertyValues(this.schedulerContext); } - pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap()); - pvs.addPropertyValues(bundle.getTrigger().getJobDataMap()); + pvs.addPropertyValues(getJobDetailDataMap(bundle)); + pvs.addPropertyValues(getTriggerDataMap(bundle)); if (this.ignoredUnknownProperties != null) { for (String propName : this.ignoredUnknownProperties) { if (pvs.contains(propName) && !bw.isWritableProperty(propName)) { @@ -104,4 +110,20 @@ public class SpringBeanJobFactory extends AdaptableJobFactory implements Schedul return (!(jobObject instanceof QuartzJobBean)); } + // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... + private JobDataMap getJobDetailDataMap(TriggerFiredBundle bundle) throws Exception { + Method getJobDetail = bundle.getClass().getMethod("getJobDetail"); + Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle); + Method getJobDataMap = jobDetail.getClass().getMethod("getJobDataMap"); + return (JobDataMap) ReflectionUtils.invokeMethod(getJobDataMap, jobDetail); + } + + // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... + private JobDataMap getTriggerDataMap(TriggerFiredBundle bundle) throws Exception { + Method getTrigger = bundle.getClass().getMethod("getTrigger"); + Object trigger = ReflectionUtils.invokeMethod(getTrigger, bundle); + Method getJobDataMap = trigger.getClass().getMethod("getJobDataMap"); + return (JobDataMap) ReflectionUtils.invokeMethod(getJobDataMap, trigger); + } + }