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 caddf3584c1..aa40358ca9f 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 @@ -30,7 +30,7 @@ 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.5+ as well as Quartz 2.0, as of Spring 3.1. + *
Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1. * * @author Juergen Hoeller * @since 2.0 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 dc3b9dffc2b..cee2683dce5 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 @@ -33,7 +33,7 @@ import org.springframework.util.Assert; * Convenience subclass of Quartz's {@link org.quartz.CronTrigger} class, * making bean-style usage easier. * - *
CronTrigger itself is already a JavaBean but lacks sensible defaults. + *
CronTrigger itself is already a JavaBean but lacks sensible defaults.
* This class uses the Spring bean name as job name, the Quartz default group
* ("DEFAULT") as job group, the current time as start time, and indefinite
* repetition, if not specified.
@@ -44,8 +44,10 @@ import org.springframework.util.Assert;
* instead of registering the JobDetail separately.
*
*
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.
+ * Use Quartz 2.0's native JobDetailImpl class or the new Quartz 2.0
+ * builder API instead. Alternatively, switch to Spring's {@link CronTriggerFactoryBean}
+ * which largely is a drop-in replacement for this class and its properties and
+ * consistently works against Quartz 1.x as well as Quartz 2.0/2.1.
*
* @author Juergen Hoeller
* @since 18.02.2004
@@ -72,6 +74,7 @@ public class CronTriggerBean extends CronTrigger
private long startDelay;
+
/**
* Register objects in the JobDataMap via a given Map.
*
These objects will be available to this Trigger only,
@@ -80,7 +83,7 @@ public class CronTriggerBean extends CronTrigger
* (for example Spring-managed beans)
* @see JobDetailBean#setJobDataAsMap
*/
- public void setJobDataAsMap(Map jobDataAsMap) {
+ public void setJobDataAsMap(Map The start delay is added to the current system time (when the bean starts)
+ * to control the {@link #setStartTime start time} of the trigger.
+ * If the start delay is non-zero, it will always
+ * take precedence over start time.
+ * @param startDelay the start delay, in milliseconds
+ */
+ public void setStartDelay(long startDelay) {
+ Assert.state(startDelay >= 0, "Start delay cannot be negative.");
+ this.startDelay = startDelay;
+ }
+
/**
* Set the JobDetail that this trigger should be associated with.
* This is typically used with a bean reference if the JobDetail
@@ -122,20 +138,6 @@ public class CronTriggerBean extends CronTrigger
this.jobDetail = jobDetail;
}
- /**
- * Set the start delay in milliseconds.
- * The start delay is added to the current system time
- * (when the bean starts) to control the {@link #setStartTime start time}
- * of the trigger.
- * If the start delay is non-zero it will always
- * take precedence over start time.
- * @param startDelay the start delay, in milliseconds.
- */
- public void setStartDelay(long startDelay) {
- Assert.state(startDelay >= 0, "Start delay cannot be negative.");
- this.startDelay = startDelay;
- }
-
public JobDetail getJobDetail() {
return this.jobDetail;
}
@@ -146,7 +148,7 @@ public class CronTriggerBean extends CronTrigger
public void afterPropertiesSet() throws Exception {
- if(this.startDelay > 0) {
+ if (this.startDelay > 0) {
setStartTime(new Date(System.currentTimeMillis() + this.startDelay));
}
diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java
new file mode 100644
index 00000000000..a8294f05d41
--- /dev/null
+++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.scheduling.quartz;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.quartz.CronTrigger;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.Constants;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.CronTrigger}
+ * instance, supporting bean-style usage for trigger configuration.
+ *
+ * This class will also register the trigger with the job name and group of
+ * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
+ * to automatically register a trigger for the corresponding JobDetail,
+ * instead of registering the JobDetail separately.
+ *
+ * NOTE: This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1,
+ * in contrast to the older {@link CronTriggerBean} class.
+ *
+ * @author Juergen Hoeller
+ * @since 3.1
+ * @see #setName
+ * @see #setGroup
+ * @see #setStartTime
+ * @see #setJobName
+ * @see #setJobGroup
+ * @see #setJobDetail
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
+ * @see org.springframework.scheduling.quartz.SimpleTriggerBean
+ */
+public class CronTriggerFactoryBean implements FactoryBean These objects will be available to this Trigger only,
+ * in contrast to objects in the JobDetail's data map.
+ * @param jobDataAsMap Map with String keys and any objects as values
+ * (for example Spring-managed beans)
+ * @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap
+ */
+ public void setJobDataAsMap(Map The start delay is added to the current system time (when the bean starts)
+ * to control the start time of the trigger.
+ * @param startDelay the start delay, in milliseconds
+ */
+ public void setStartDelay(long startDelay) {
+ Assert.state(startDelay >= 0, "Start delay cannot be negative.");
+ this.startDelay = startDelay;
+ }
+
+ /**
+ * Specify the cron expression for this trigger.
+ */
+ public void setCronExpression(String cronExpression) {
+ this.cronExpression = cronExpression;
+ }
+
+ /**
+ * Specify the time zone for this trigger's cron expression.
+ */
+ public void setTimeZone(TimeZone timeZone) {
+ this.timeZone = timeZone;
+ }
+
+ /**
+ * Specify the priority of this trigger.
+ */
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Specify a misfire instruction for this trigger.
+ */
+ public void setMisfireInstruction(int misfireInstruction) {
+ this.misfireInstruction = misfireInstruction;
+ }
+
+ /**
+ * Set the misfire instruction via the name of the corresponding
+ * constant in the {@link org.quartz.CronTrigger} class.
+ * Default is NOTE: As of Quartz 2.0, the recommended strategy is to define an
+ * entry of name "jobDetail" and type JobDetail in the trigger's JobDataMap.
+ *
* @author Juergen Hoeller
* @since 18.02.2004
* @see SchedulerFactoryBean#setTriggers
@@ -38,6 +41,12 @@ import org.quartz.JobDetail;
*/
public interface JobDetailAwareTrigger {
+ /**
+ * Name of the key for the JobDetail value in the trigger's JobDataMap.
+ * This is an alternative to implementing the JobDetailAwareTrigger interface.
+ */
+ String JOB_DETAIL_KEY = "jobDetail";
+
/**
* Return the JobDetail that this Trigger is associated with.
* @return the associated JobDetail, or NOTE: This convenience subclass does not work against Quartz 2.0.
- * Use Quartz 2.0's native NOTE: This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1,
+ * in contrast to the older {@link JobDetailBean} class.
+ *
+ * @author Juergen Hoeller
+ * @since 3.1
+ * @see #setName
+ * @see #setGroup
+ * @see org.springframework.beans.factory.BeanNameAware
+ * @see org.quartz.Scheduler#DEFAULT_GROUP
+ */
+public class JobDetailFactoryBean
+ implements FactoryBean These objects will be available to this Job only,
+ * in contrast to objects in the SchedulerContext.
+ * Note: When using persistent Jobs whose JobDetail will be kept in the
+ * database, do not put Spring-managed beans or an ApplicationContext
+ * reference into the JobDataMap but rather into the SchedulerContext.
+ * @param jobDataAsMap Map with String keys and any objects as values
+ * (for example Spring-managed beans)
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setSchedulerContextAsMap
+ */
+ public void setJobDataAsMap(Map In case of a QuartzJobBean, the reference will be applied to the Job
+ * instance as bean property. An "applicationContext" attribute will correspond
+ * to a "setApplicationContext" method in that scenario.
+ * Note that BeanFactory callback interfaces like ApplicationContextAware
+ * are not automatically applied to Quartz Job instances, because Quartz
+ * itself is responsible for the lifecycle of its Jobs.
+ * Note: When using persistent job stores where JobDetail contents will
+ * be kept in the database, do not put an ApplicationContext reference into
+ * the JobDataMap but rather into the SchedulerContext.
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setApplicationContextSchedulerContextKey
+ * @see org.springframework.context.ApplicationContext
+ */
+ public void setApplicationContextJobDataKey(String applicationContextJobDataKey) {
+ this.applicationContextJobDataKey = applicationContextJobDataKey;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public void afterPropertiesSet() {
+ if (this.name == null) {
+ this.name = this.beanName;
+ }
+ if (this.group == null) {
+ this.group = Scheduler.DEFAULT_GROUP;
+ }
+ if (this.applicationContextJobDataKey != null) {
+ if (this.applicationContext == null) {
+ throw new IllegalStateException(
+ "JobDetailBean needs to be set up in an ApplicationContext " +
+ "to be able to handle an 'applicationContextJobDataKey'");
+ }
+ getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
+ }
+
+ /*
+ JobDetailImpl jdi = new JobDetailImpl();
+ jdi.setName(this.name);
+ jdi.setGroup(this.group);
+ jdi.setJobClass(this.jobClass);
+ jdi.setJobDataMap(this.jobDataMap);
+ this.jobDetail = jdi;
+ */
+
+ Class jobDetailClass;
+ try {
+ jobDetailClass = getClass().getClassLoader().loadClass("org.quartz.impl.JobDetailImpl");
+ }
+ catch (ClassNotFoundException ex) {
+ jobDetailClass = JobDetail.class;
+ }
+ BeanWrapper bw = new BeanWrapperImpl(jobDetailClass);
+ MutablePropertyValues pvs = new MutablePropertyValues();
+ pvs.add("name", this.name);
+ pvs.add("group", this.group);
+ pvs.add("jobClass", this.jobClass);
+ pvs.add("jobDataMap", this.jobDataMap);
+ bw.setPropertyValues(pvs);
+ this.jobDetail = (JobDetail) bw.getWrappedInstance();
+ }
+
+
+ public JobDetail getObject() {
+ return this.jobDetail;
+ }
+
+ public Class> getObjectType() {
+ return JobDetail.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
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 d5b77b25707..2333e7d57c6 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
@@ -65,7 +65,7 @@ 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.
+ * Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
*
* @author Juergen Hoeller
* @author Alef Arendsen
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 d10468aaf95..a28111c0042 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
@@ -51,7 +51,7 @@ import org.springframework.util.ReflectionUtils;
* 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.
+ * Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
*
* @author Juergen Hoeller
* @since 2.5.6
@@ -359,8 +359,8 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
boolean triggerExists = triggerExists(trigger);
if (!triggerExists || this.overwriteExistingJobs) {
// Check if the Trigger is aware of an associated JobDetail.
- if (trigger instanceof JobDetailAwareTrigger) {
- JobDetail jobDetail = ((JobDetailAwareTrigger) trigger).getJobDetail();
+ JobDetail jobDetail = findJobDetail(trigger);
+ if (jobDetail != null) {
// Automatically register the JobDetail too.
if (!this.jobDetails.contains(jobDetail) && addJobToScheduler(jobDetail)) {
this.jobDetails.add(jobDetail);
@@ -390,6 +390,21 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
}
}
+ private JobDetail findJobDetail(Trigger trigger) {
+ if (trigger instanceof JobDetailAwareTrigger) {
+ return ((JobDetailAwareTrigger) trigger).getJobDetail();
+ }
+ else {
+ try {
+ Map jobDataMap = (Map) ReflectionUtils.invokeMethod(Trigger.class.getMethod("getJobDataMap"), trigger);
+ return (JobDetail) jobDataMap.get(JobDetailAwareTrigger.JOB_DETAIL_KEY);
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalStateException("Inconsistent Quartz API: " + ex);
+ }
+ }
+ }
+
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
private boolean jobDetailExists(JobDetail jobDetail) throws SchedulerException {
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 44d7f18ca7c..d02689462da 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
@@ -29,7 +29,7 @@ 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.
+ * Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
*
* @author Juergen Hoeller
* @since 2.5.6
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 5dde075eda9..966ad09b74d 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,7 +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.
*
- * Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1.
+ * Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, 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 acda87ddb9e..ca9d44c6586 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
@@ -32,7 +32,7 @@ import org.springframework.core.Constants;
* Convenience subclass of Quartz's {@link org.quartz.SimpleTrigger} class,
* making bean-style usage easier.
*
- * SimpleTrigger itself is already a JavaBean but lacks sensible defaults.
+ * NOTE: This convenience subclass does not work against Quartz 2.0.
- * Use Quartz 2.0's native This delay will just be applied if no custom start time was
- * specified. However, in typical usage within a Spring context,
- * the start time will be the container startup time anyway.
- * Specifying a relative delay is appropriate in that case.
- * @see #setStartTime
+ * Set the start delay in milliseconds.
+ * The start delay is added to the current system time (when the bean starts)
+ * to control the {@link #setStartTime start time} of the trigger.
+ * If the start delay is non-zero, it will always
+ * take precedence over start time.
+ * @param startDelay the start delay, in milliseconds
*/
public void setStartDelay(long startDelay) {
this.startDelay = startDelay;
diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java
new file mode 100644
index 00000000000..c26b0d00424
--- /dev/null
+++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.scheduling.quartz;
+
+import java.lang.reflect.Method;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Map;
+
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SimpleTrigger;
+
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.Constants;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.SimpleTrigger}
+ * instance, supporting bean-style usage for trigger configuration.
+ *
+ * This class will also register the trigger with the job name and group of
+ * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
+ * to automatically register a trigger for the corresponding JobDetail,
+ * instead of registering the JobDetail separately.
+ *
+ * NOTE: This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1,
+ * in contrast to the older {@link SimpleTriggerBean} class.
+ *
+ * @author Juergen Hoeller
+ * @since 3.1
+ * @see #setName
+ * @see #setGroup
+ * @see #setStartTime
+ * @see #setJobName
+ * @see #setJobGroup
+ * @see #setJobDetail
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
+ * @see org.springframework.scheduling.quartz.CronTriggerBean
+ */
+public class SimpleTriggerFactoryBean implements FactoryBean These objects will be available to this Trigger only,
+ * in contrast to objects in the JobDetail's data map.
+ * @param jobDataAsMap Map with String keys and any objects as values
+ * (for example Spring-managed beans)
+ * @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap
+ */
+ public void setJobDataAsMap(Map The start delay is added to the current system time (when the bean starts)
+ * to control the start time of the trigger.
+ * @param startDelay the start delay, in milliseconds
+ */
+ public void setStartDelay(long startDelay) {
+ Assert.state(startDelay >= 0, "Start delay cannot be negative.");
+ this.startDelay = startDelay;
+ }
+
+ /**
+ * Specify the interval between execution times of this trigger.
+ */
+ public void setRepeatInterval(long repeatInterval) {
+ this.repeatInterval = repeatInterval;
+ }
+
+ /**
+ * Specify the priority of this trigger.
+ */
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Specify a misfire instruction for this trigger.
+ */
+ public void setMisfireInstruction(int misfireInstruction) {
+ this.misfireInstruction = misfireInstruction;
+ }
+
+ /**
+ * Set the misfire instruction via the name of the corresponding
+ * constant in the {@link org.quartz.SimpleTrigger} class.
+ * Default is Compatible with Quartz 1.5+ as well as Quartz 2.0, as of Spring 3.1.
+ * Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
*
* @author Juergen Hoeller
* @since 2.0
CronTrigger(Impl) itself is already a JavaBean but lacks sensible defaults.
+ * This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT")
+ * as job group, the current time as start time, and indefinite repetition, if not specified.
+ *
+ * MISFIRE_INSTRUCTION_SMART_POLICY.
+ * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
+ * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
+ * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
+ */
+ public void setMisfireInstructionName(String constantName) {
+ this.misfireInstruction = constants.asNumber(constantName).intValue();
+ }
+
+ public void setBeanName(String beanName) {
+ this.beanName = beanName;
+ }
+
+
+ public void afterPropertiesSet() {
+ if (this.name == null) {
+ this.name = this.beanName;
+ }
+ if (this.group == null) {
+ this.group = Scheduler.DEFAULT_GROUP;
+ }
+ if (this.jobDetail != null) {
+ this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
+ }
+ if (this.startDelay > 0) {
+ this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
+ }
+ else if (this.startTime == null) {
+ this.startTime = new Date();
+ }
+ if (this.timeZone == null) {
+ this.timeZone = TimeZone.getDefault();
+ }
+
+ /*
+ CronTriggerImpl cti = new CronTriggerImpl();
+ cti.setName(this.name);
+ cti.setGroup(this.group);
+ cti.setJobKey(this.jobDetail.getKey());
+ cti.setJobDataMap(this.jobDataMap);
+ cti.setStartTime(this.startTime);
+ cti.setCronExpression(this.cronExpression);
+ cti.setTimeZone(this.timeZone);
+ cti.setPriority(this.priority);
+ cti.setMisfireInstruction(this.misfireInstruction);
+ this.cronTrigger = cti;
+ */
+
+ Class cronTriggerClass;
+ Method jobKeyMethod;
+ try {
+ cronTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.CronTriggerImpl");
+ jobKeyMethod = JobDetail.class.getMethod("getKey");
+ }
+ catch (ClassNotFoundException ex) {
+ cronTriggerClass = CronTrigger.class;
+ jobKeyMethod = null;
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalStateException("Incompatible Quartz version");
+ }
+ BeanWrapper bw = new BeanWrapperImpl(cronTriggerClass);
+ MutablePropertyValues pvs = new MutablePropertyValues();
+ pvs.add("name", this.name);
+ pvs.add("group", this.group);
+ if (jobKeyMethod != null) {
+ pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail));
+ }
+ else {
+ pvs.add("jobName", this.jobDetail.getName());
+ pvs.add("jobGroup", this.jobDetail.getGroup());
+ }
+ pvs.add("jobDataMap", this.jobDataMap);
+ pvs.add("startTime", this.startTime);
+ pvs.add("cronExpression", this.cronExpression);
+ pvs.add("timeZone", this.timeZone);
+ pvs.add("priority", this.priority);
+ pvs.add("misfireInstruction", this.misfireInstruction);
+ bw.setPropertyValues(pvs);
+ this.cronTrigger = (CronTrigger) bw.getWrappedInstance();
+ }
+
+
+ public CronTrigger getObject() {
+ return this.cronTrigger;
+ }
+
+ public Class> getObjectType() {
+ return CronTrigger.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java
index 3da4c9c271e..dcbfdc12dc8 100644
--- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java
+++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java
@@ -1,12 +1,12 @@
/*
- * Copyright 2002-2005 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.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -29,6 +29,9 @@ import org.quartz.JobDetail;
* This involves the need to register the JobDetail object separately
* with SchedulerFactoryBean.
*
+ * null if none
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 8ff97295ed4..34c8395c93b 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
@@ -36,8 +36,10 @@ import org.springframework.context.ApplicationContextAware;
* and the Quartz default group ("DEFAULT") as job group if not specified.
*
* JobDetailImpl class or the new
- * Quartz 2.0 builder API instead.
+ * Use Quartz 2.0's native JobDetailImpl class or the new Quartz 2.0
+ * builder API instead. Alternatively, switch to Spring's {@link JobDetailFactoryBean}
+ * which largely is a drop-in replacement for this class and its properties and
+ * consistently works against Quartz 1.x as well as Quartz 2.0/2.1.
*
* @author Juergen Hoeller
* @since 18.02.2004
diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java
new file mode 100644
index 00000000000..d779c102064
--- /dev/null
+++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.scheduling.quartz;
+
+import java.util.Map;
+
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.JobDetail}
+ * instance, supporting bean-style usage for JobDetail configuration.
+ *
+ * JobDetail(Impl) itself is already a JavaBean but lacks
+ * sensible defaults. This class uses the Spring bean name as job name,
+ * and the Quartz default group ("DEFAULT") as job group if not specified.
+ *
+ * SimpleTrigger itself is already a JavaBean but lacks sensible defaults.
* This class uses the Spring bean name as job name, the Quartz default group
* ("DEFAULT") as job group, the current time as start time, and indefinite
* repetition, if not specified.
@@ -43,8 +43,10 @@ import org.springframework.core.Constants;
* instead of registering the JobDetail separately.
*
* SimpleTriggerImpl class or the new
- * Quartz 2.0 builder API instead.
+ * Use Quartz 2.0's native JobDetailImpl class or the new Quartz 2.0
+ * builder API instead. Alternatively, switch to Spring's {@link SimpleTriggerFactoryBean}
+ * which largely is a drop-in replacement for this class and its properties and
+ * consistently works against Quartz 1.x as well as Quartz 2.0/2.1.
*
* @author Juergen Hoeller
* @since 18.02.2004
@@ -84,7 +86,7 @@ public class SimpleTriggerBean extends SimpleTrigger
* (for example Spring-managed beans)
* @see JobDetailBean#setJobDataAsMap
*/
- public void setJobDataAsMap(Map jobDataAsMap) {
+ public void setJobDataAsMap(MapSimpleTrigger(Impl) itself is already a JavaBean but lacks sensible defaults.
+ * This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT")
+ * as job group, the current time as start time, and indefinite repetition, if not specified.
+ *
+ * MISFIRE_INSTRUCTION_SMART_POLICY.
+ * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW
+ * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
+ * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
+ * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
+ * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
+ * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
+ */
+ public void setMisfireInstructionName(String constantName) {
+ this.misfireInstruction = constants.asNumber(constantName).intValue();
+ }
+
+ public void setBeanName(String beanName) {
+ this.beanName = beanName;
+ }
+
+
+ public void afterPropertiesSet() throws ParseException {
+ if (this.name == null) {
+ this.name = this.beanName;
+ }
+ if (this.group == null) {
+ this.group = Scheduler.DEFAULT_GROUP;
+ }
+ if (this.jobDetail != null) {
+ this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
+ }
+ if (this.startDelay > 0) {
+ this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
+ }
+ else if (this.startTime == null) {
+ this.startTime = new Date();
+ }
+
+ /*
+ SimpleTriggerImpl sti = new SimpleTriggerImpl();
+ sti.setName(this.name);
+ sti.setGroup(this.group);
+ sti.setJobKey(this.jobDetail.getKey());
+ sti.setJobDataMap(this.jobDataMap);
+ sti.setStartTime(this.startTime);
+ sti.setRepeatInterval(this.repeatInterval);
+ sti.setPriority(this.priority);
+ sti.setMisfireInstruction(this.misfireInstruction);
+ this.simpleTrigger = sti;
+ */
+
+ Class simpleTriggerClass;
+ Method jobKeyMethod;
+ try {
+ simpleTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.SimpleTriggerImpl");
+ jobKeyMethod = JobDetail.class.getMethod("getKey");
+ }
+ catch (ClassNotFoundException ex) {
+ simpleTriggerClass = SimpleTrigger.class;
+ jobKeyMethod = null;
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalStateException("Incompatible Quartz version");
+ }
+ BeanWrapper bw = new BeanWrapperImpl(simpleTriggerClass);
+ MutablePropertyValues pvs = new MutablePropertyValues();
+ pvs.add("name", this.name);
+ pvs.add("group", this.group);
+ if (jobKeyMethod != null) {
+ pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail));
+ }
+ else {
+ pvs.add("jobName", this.jobDetail.getName());
+ pvs.add("jobGroup", this.jobDetail.getGroup());
+ }
+ pvs.add("jobDataMap", this.jobDataMap);
+ pvs.add("startTime", this.startTime);
+ pvs.add("repeatInterval", this.repeatInterval);
+ pvs.add("repeatCount", -1);
+ pvs.add("priority", this.priority);
+ pvs.add("misfireInstruction", this.misfireInstruction);
+ bw.setPropertyValues(pvs);
+ this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance();
+ }
+
+
+ public SimpleTrigger getObject() {
+ return this.simpleTrigger;
+ }
+
+ public Class> getObjectType() {
+ return SimpleTrigger.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+}
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 dfcd158d9f9..5e555e2d04d 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
@@ -37,7 +37,7 @@ import org.springframework.util.ReflectionUtils;
* 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.
*
- *