Require Jackson 2.0+, EhCache 2.5+, Quartz 2.1.4+

Issue: SPR-11262
This commit is contained in:
Juergen Hoeller
2014-03-27 21:59:23 +01:00
parent 5c577451f3
commit ea1e27efa2
39 changed files with 332 additions and 4186 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -52,9 +52,8 @@ import org.springframework.util.ClassUtils;
* <p>Note: If the named Cache instance is found, the properties will be ignored and the
* Cache instance will be retrieved from the CacheManager.
*
* <p>Note: As of Spring 4.0, Spring's EhCache support requires EhCache 2.1 or higher.
* We recommend the use of EhCache 2.5 or higher.
* <p>Note: As of Spring 4.1, Spring's EhCache support requires EhCache 2.5 or higher.
*
* @author Juergen Hoeller
* @author Dmitriy Kopylenko
* @since 1.1.1
@@ -92,10 +91,8 @@ public class EhCacheFactoryBean extends CacheConfiguration implements FactoryBea
private Ehcache cache;
@SuppressWarnings("deprecation")
public EhCacheFactoryBean() {
// Using deprecated setMaxElementsInMemory method for EhCache 2.1-2.4 compatibility
setMaxElementsInMemory(10000);
setMaxEntriesLocalHeap(10000);
setMaxElementsOnDisk(10000000);
setTimeToLiveSeconds(120);
setTimeToIdleSeconds(120);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -44,8 +44,7 @@ import org.springframework.core.io.Resource;
* and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
* also necessary for loading EhCache configuration from a non-default config location.
*
* <p>Note: As of Spring 4.0, Spring's EhCache support requires EhCache 2.1 or higher.
* We recommend the use of EhCache 2.5 or higher.
* <p>Note: As of Spring 4.1, Spring's EhCache support requires EhCache 2.5 or higher.
*
* @author Juergen Hoeller
* @author Dmitriy Kopylenko

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,22 +16,17 @@
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.
*
* <p>Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0.
* <b>Note:</b> Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+.
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.0
@@ -40,19 +35,8 @@ import org.springframework.util.ReflectionUtils;
*/
public class AdaptableJobFactory implements JobFactory {
/**
* Quartz 2.0 version of newJob: simply delegates to old newJob variant.
* @see #newJob(org.quartz.spi.TriggerFiredBundle)
*/
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
return newJob(bundle);
}
/**
* Quartz 1.x version of newJob: contains actual implementation code.
*/
@Override
public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
try {
Object jobObject = createJobInstance(bundle);
return adaptJob(jobObject);
@@ -71,12 +55,7 @@ public class AdaptableJobFactory implements JobFactory {
* @throws Exception if job instantiation failed
*/
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 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();
return bundle.getJobDetail().getJobClass().newInstance();
}
/**

View File

@@ -1,175 +0,0 @@
/*
* Copyright 2002-2013 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.Date;
import java.util.Map;
import java.util.TimeZone;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Constants;
import org.springframework.util.Assert;
/**
* Convenience subclass of Quartz's {@link org.quartz.CronTrigger} class,
* making bean-style usage easier.
*
* <p>{@code 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.
*
* <p>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.
*
* <p><b>NOTE: This convenience subclass does not work against Quartz 2.0.</b>
* Use Quartz 2.0's native {@code 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.x.
*
* @author Juergen Hoeller
* @since 18.02.2004
* @see #setName
* @see #setGroup
* @see #setStartTime
* @see #setJobName
* @see #setJobGroup
* @see #setJobDetail
* @see SchedulerFactoryBean#setTriggers
* @see SchedulerFactoryBean#setJobDetails
* @see SimpleTriggerBean
*/
@SuppressWarnings("serial")
public class CronTriggerBean extends CronTrigger
implements JobDetailAwareTrigger, BeanNameAware, InitializingBean {
/** Constants for the CronTrigger class */
private static final Constants constants = new Constants(CronTrigger.class);
private JobDetail jobDetail;
private String beanName;
private long startDelay = 0;
/**
* Register objects in the JobDataMap via a given Map.
* <p>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 JobDetailBean#setJobDataAsMap
*/
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
getJobDataMap().putAll(jobDataAsMap);
}
/**
* Set the misfire instruction via the name of the corresponding
* constant in the {@link org.quartz.CronTrigger} class.
* Default is {@code 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) {
setMisfireInstruction(constants.asNumber(constantName).intValue());
}
/**
* Set a list of TriggerListener names for this job, referring to
* non-global TriggerListeners registered with the Scheduler.
* <p>A TriggerListener name always refers to the name returned
* by the TriggerListener implementation.
* @see SchedulerFactoryBean#setTriggerListeners
* @see org.quartz.TriggerListener#getName
* @deprecated as of Spring 4.0, since it only works on Quartz 1.x
*/
@Deprecated
public void setTriggerListenerNames(String... names) {
for (String name : names) {
addTriggerListener(name);
}
}
/**
* Set the start delay in milliseconds.
* <p>The start delay is added to the current system time (when the bean starts)
* to control the {@link #setStartTime start time} of the trigger.
* <p>If the start delay is non-zero, it will <strong>always</strong>
* 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.
* <p>This is typically used with a bean reference if the JobDetail
* is a Spring-managed bean. Alternatively, the trigger can also
* be associated with a job by name and group.
* @see #setJobName
* @see #setJobGroup
*/
public void setJobDetail(JobDetail jobDetail) {
this.jobDetail = jobDetail;
}
@Override
public JobDetail getJobDetail() {
return this.jobDetail;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void afterPropertiesSet() {
if (getName() == null) {
setName(this.beanName);
}
if (getGroup() == null) {
setGroup(Scheduler.DEFAULT_GROUP);
}
if (this.startDelay > 0 || getStartTime() == null) {
setStartTime(new Date(System.currentTimeMillis() + this.startDelay));
}
if (getTimeZone() == null) {
setTimeZone(TimeZone.getDefault());
}
if (this.jobDetail != null) {
setJobName(this.jobDetail.getName());
setJobGroup(this.jobDetail.getGroup());
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,7 +16,7 @@
package org.springframework.scheduling.quartz;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
@@ -25,16 +25,13 @@ import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.impl.triggers.CronTriggerImpl;
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}
@@ -49,9 +46,6 @@ import org.springframework.util.ReflectionUtils;
* to automatically register a trigger for the corresponding JobDetail,
* instead of registering the JobDetail separately.
*
* <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.x,
* in contrast to the older {@link CronTriggerBean} class.
*
* @author Juergen Hoeller
* @since 3.1
* @see #setName
@@ -60,7 +54,6 @@ import org.springframework.util.ReflectionUtils;
* @see #setJobDetail
* @see SchedulerFactoryBean#setTriggers
* @see SchedulerFactoryBean#setJobDetails
* @see SimpleTriggerBean
*/
public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNameAware, InitializingBean {
@@ -139,7 +132,6 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
* 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<String, ?> jobDataAsMap) {
this.jobDataMap.putAll(jobDataAsMap);
@@ -225,7 +217,7 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
@Override
public void afterPropertiesSet() {
public void afterPropertiesSet() throws ParseException {
if (this.name == null) {
this.name = this.beanName;
}
@@ -233,7 +225,7 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
this.group = Scheduler.DEFAULT_GROUP;
}
if (this.jobDetail != null) {
this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
this.jobDataMap.put("jobDetail", this.jobDetail);
}
if (this.startDelay > 0 || this.startTime == null) {
this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
@@ -242,7 +234,6 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
this.timeZone = TimeZone.getDefault();
}
/*
CronTriggerImpl cti = new CronTriggerImpl();
cti.setName(this.name);
cti.setGroup(this.group);
@@ -256,42 +247,6 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
cti.setMisfireInstruction(this.misfireInstruction);
cti.setDescription(this.description);
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("calendarName", this.calendarName);
pvs.add("priority", this.priority);
pvs.add("misfireInstruction", this.misfireInstruction);
pvs.add("description", this.description);
bw.setPropertyValues(pvs);
this.cronTrigger = (CronTrigger) bw.getWrappedInstance();
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2002-2012 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 org.quartz.JobDetail;
/**
* Interface to be implemented by Quartz Triggers that are aware
* of the JobDetail object that they are associated with.
*
* <p>SchedulerFactoryBean will auto-detect Triggers that implement this
* interface and register them for the respective JobDetail accordingly.
*
* <p>The alternative is to configure a Trigger for a Job name and group:
* This involves the need to register the JobDetail object separately
* with SchedulerFactoryBean.
*
* <p><b>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
* @see SchedulerFactoryBean#setJobDetails
* @see org.quartz.Trigger#setJobName
* @see org.quartz.Trigger#setJobGroup
*/
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 {@code null} if none
*/
JobDetail getJobDetail();
}

View File

@@ -1,169 +0,0 @@
/*
* Copyright 2002-2013 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.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Convenience subclass of Quartz's {@link org.quartz.JobDetail} class,
* making bean-style usage easier.
*
* <p>{@code JobDetail} 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.
*
* <p><b>NOTE: This convenience subclass does not work against Quartz 2.0.</b>
* Use Quartz 2.0's native {@code 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.x.
*
* @author Juergen Hoeller
* @since 18.02.2004
* @see #setName
* @see #setGroup
* @see org.springframework.beans.factory.BeanNameAware
* @see org.quartz.Scheduler#DEFAULT_GROUP
*/
@SuppressWarnings({"serial", "rawtypes"})
public class JobDetailBean extends JobDetail
implements BeanNameAware, ApplicationContextAware, InitializingBean {
private Class<?> actualJobClass;
private String beanName;
private ApplicationContext applicationContext;
private String applicationContextJobDataKey;
/**
* Overridden to support any job class, to allow a custom JobFactory
* to adapt the given job class to the Quartz Job interface.
* @see SchedulerFactoryBean#setJobFactory
*/
@Override
public void setJobClass(Class jobClass) {
if (jobClass != null && !Job.class.isAssignableFrom(jobClass)) {
super.setJobClass(DelegatingJob.class);
this.actualJobClass = jobClass;
}
else {
super.setJobClass(jobClass);
}
}
/**
* Overridden to support any job class, to allow a custom JobFactory
* to adapt the given job class to the Quartz Job interface.
*/
@Override
public Class getJobClass() {
return (this.actualJobClass != null ? this.actualJobClass : super.getJobClass());
}
/**
* Register objects in the JobDataMap via a given Map.
* <p>These objects will be available to this Job only,
* in contrast to objects in the SchedulerContext.
* <p>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 SchedulerFactoryBean#setSchedulerContextAsMap
*/
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
getJobDataMap().putAll(jobDataAsMap);
}
/**
* Set a list of JobListener names for this job, referring to
* non-global JobListeners registered with the Scheduler.
* <p>A JobListener name always refers to the name returned
* by the JobListener implementation.
* @see SchedulerFactoryBean#setJobListeners
* @see org.quartz.JobListener#getName
* @deprecated as of Spring 4.0, since it only works on Quartz 1.x
*/
@Deprecated
public void setJobListenerNames(String... names) {
for (String name : names) {
addJobListener(name);
}
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Set the key of an ApplicationContext reference to expose in the JobDataMap,
* for example "applicationContext". Default is none.
* Only applicable when running in a Spring ApplicationContext.
* <p>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.
* <p>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.
* <p><b>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.</b>
* @see SchedulerFactoryBean#setApplicationContextSchedulerContextKey
* @see org.springframework.context.ApplicationContext
*/
public void setApplicationContextJobDataKey(String applicationContextJobDataKey) {
this.applicationContextJobDataKey = applicationContextJobDataKey;
}
@Override
public void afterPropertiesSet() {
if (getName() == null) {
setName(this.beanName);
}
if (getGroup() == null) {
setGroup(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);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -21,10 +21,8 @@ import java.util.Map;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.impl.JobDetailImpl;
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;
@@ -39,9 +37,6 @@ 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.
*
* <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.x,
* in contrast to the older {@link JobDetailBean} class.
*
* @author Juergen Hoeller
* @since 3.1
* @see #setName
@@ -181,6 +176,7 @@ public class JobDetailFactoryBean
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() {
if (this.name == null) {
this.name = this.beanName;
@@ -197,35 +193,15 @@ public class JobDetailFactoryBean
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
}
/*
JobDetailImpl jdi = new JobDetailImpl();
jdi.setName(this.name);
jdi.setGroup(this.group);
jdi.setJobClass(this.jobClass);
jdi.setJobClass((Class) this.jobClass);
jdi.setJobDataMap(this.jobDataMap);
jdi.setDurability(this.durability);
jdi.setRequestsRecovery(this.requestsRecovery);
jdi.setDescription(this.description);
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);
pvs.add("durability", this.durability);
pvs.add("requestsRecovery", this.requestsRecovery);
pvs.add("description", this.description);
bw.setPropertyValues(pvs);
this.jobDetail = (JobDetail) bw.getWrappedInstance();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -87,7 +87,7 @@ public class LocalTaskExecutorThreadPool implements ThreadPool {
@Override
public int blockForAvailableThreads() {
// The present implementation always returns 1, making Quartz (1.6)
// The present implementation always returns 1, making Quartz
// always schedule any tasks that it feels like scheduling.
// This could be made smarter for specific TaskExecutors,
// for example calling {@code getMaximumPoolSize() - getActiveCount()}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -17,20 +17,17 @@
package org.springframework.scheduling.quartz;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobDataMap;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.quartz.impl.JobDetailImpl;
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;
@@ -41,7 +38,6 @@ import org.springframework.beans.support.ArgumentConvertingMethodInvoker;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MethodInvoker;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} that exposes a
@@ -67,8 +63,7 @@ import org.springframework.util.ReflectionUtils;
* 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.
*
* <p>Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0.
* <b>Note:</b> Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+.
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @author Alef Arendsen
@@ -81,28 +76,6 @@ import org.springframework.util.ReflectionUtils;
public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethodInvoker
implements FactoryBean<JobDetail>, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean {
private static Class<?> jobDetailImplClass;
private static Method setResultMethod;
static {
try {
jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl");
}
catch (ClassNotFoundException ex) {
jobDetailImplClass = null;
}
try {
Class<?> jobExecutionContextClass =
QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext");
setResultMethod = jobExecutionContextClass.getMethod("setResult", Object.class);
}
catch (Exception ex) {
throw new IllegalStateException("Incompatible Quartz API: " + ex);
}
}
private String name;
private String group = Scheduler.DEFAULT_GROUP;
@@ -111,8 +84,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
private String targetBeanName;
private String[] jobListenerNames;
private String beanName;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@@ -125,7 +96,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
/**
* Set the name of the job.
* <p>Default is the bean name of this FactoryBean.
* @see org.quartz.JobDetail#setName
*/
public void setName(String name) {
this.name = name;
@@ -134,7 +104,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
/**
* Set the group of the job.
* <p>Default is the default group of the Scheduler.
* @see org.quartz.JobDetail#setGroup
* @see org.quartz.Scheduler#DEFAULT_GROUP
*/
public void setGroup(String group) {
@@ -142,9 +111,10 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
}
/**
* Specify whether or not multiple jobs should be run in a concurrent
* fashion. The behavior when one does not want concurrent jobs to be
* executed is realized through adding the {@link StatefulJob} interface.
* Specify whether or not multiple jobs should be run in a concurrent fashion.
* The behavior when one does not want concurrent jobs to be executed is
* realized through adding the {@code @PersistJobDataAfterExecution} and
* {@code @DisallowConcurrentExecution} markers.
* More information on stateful versus stateless jobs can be found
* <a href="http://www.quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/tutorial-lesson-03">here</a>.
* <p>The default setting is to run jobs concurrently.
@@ -165,20 +135,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
this.targetBeanName = targetBeanName;
}
/**
* Set a list of JobListener names for this job, referring to
* non-global JobListeners registered with the Scheduler.
* <p>A JobListener name always refers to the name returned
* by the JobListener implementation.
* @see SchedulerFactoryBean#setJobListeners
* @see org.quartz.JobListener#getName
* @deprecated as of Spring 4.0, since it only works on Quartz 1.x
*/
@Deprecated
public void setJobListenerNames(String... names) {
this.jobListenerNames = names;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
@@ -201,6 +157,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
prepare();
@@ -211,34 +168,13 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
// Build JobDetail instance.
if (jobDetailImplClass != null) {
// Using Quartz 2.0 JobDetailImpl class...
this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.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);
}
}
JobDetailImpl jdi = new JobDetailImpl();
jdi.setName(name);
jdi.setGroup(group);
jdi.setJobClass((Class) jobClass);
jdi.setDurability(true);
jdi.getJobDataMap().put("methodInvoker", this);
this.jobDetail = jdi;
postProcessJobDetail(this.jobDetail);
}
@@ -318,7 +254,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
ReflectionUtils.invokeMethod(setResultMethod, context, this.methodInvoker.invoke());
context.setResult(this.methodInvoker.invoke());
}
catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof JobExecutionException) {
@@ -343,7 +279,9 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
* Quartz checks whether or not jobs are stateful and if so,
* won't let jobs interfere with each other.
*/
public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob {
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public static class StatefulMethodInvokingJob extends MethodInvokingJob {
// No implementation, just an addition of the tag interface StatefulJob
// in order to allow stateful method invoking jobs.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,19 +16,14 @@
package org.springframework.scheduling.quartz;
import java.lang.reflect.Method;
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.util.ReflectionUtils;
/**
* Simple implementation of the Quartz Job interface, applying the
@@ -43,15 +38,9 @@ import org.springframework.util.ReflectionUtils;
* i.e. a method "setMyParam(int)". This will also work for complex
* types like business objects etc.
*
* <p>Note: The QuartzJobBean class itself only implements the standard
* Quartz {@link org.quartz.Job} interface. Let your subclass explicitly
* implement the Quartz {@link org.quartz.StatefulJob} interface to
* mark your concrete job bean as stateful.
*
* <p><b>Note that as of Spring 2.0 and Quartz 1.5, the preferred way
* to apply dependency injection to Job instances is via a JobFactory:</b>
* that is, to specify {@link SpringBeanJobFactory} as Quartz JobFactory
* (typically via
* <p><b>Note that the preferred way to apply dependency injection
* to Job instances is via a JobFactory:</b> that is, to specify
* {@link SpringBeanJobFactory} as Quartz JobFactory (typically via
* {@link SchedulerFactoryBean#setJobFactory} SchedulerFactoryBean's "jobFactory" property}).
* This allows to implement dependency-injected Quartz Jobs without
* a dependency on Spring base classes.
@@ -60,33 +49,12 @@ import org.springframework.util.ReflectionUtils;
* @since 18.02.2004
* @see org.quartz.JobExecutionContext#getMergedJobDataMap()
* @see org.quartz.Scheduler#getContext()
* @see JobDetailBean#setJobDataAsMap
* @see SimpleTriggerBean#setJobDataAsMap
* @see CronTriggerBean#setJobDataAsMap
* @see SchedulerFactoryBean#setSchedulerContextAsMap
* @see SpringBeanJobFactory
* @see SchedulerFactoryBean#setJobFactory
*/
public abstract class QuartzJobBean implements Job {
private static final Method getSchedulerMethod;
private static final Method getMergedJobDataMapMethod;
static {
try {
Class<?> jobExecutionContextClass =
QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext");
getSchedulerMethod = jobExecutionContextClass.getMethod("getScheduler");
getMergedJobDataMapMethod = jobExecutionContextClass.getMethod("getMergedJobDataMap");
}
catch (Exception ex) {
throw new IllegalStateException("Incompatible Quartz API: " + ex);
}
}
/**
* This implementation applies the passed-in job data map as bean property
* values, and delegates to {@code executeInternal} afterwards.
@@ -95,14 +63,10 @@ public abstract class QuartzJobBean implements Job {
@Override
public final void execute(JobExecutionContext context) throws JobExecutionException {
try {
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context);
Map<?, ?> mergedJobDataMap = (Map<?, ?>) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValues(scheduler.getContext());
pvs.addPropertyValues(mergedJobDataMap);
pvs.addPropertyValues(context.getScheduler().getContext());
pvs.addPropertyValues(context.getMergedJobDataMap());
bw.setPropertyValues(pvs, true);
}
catch (SchedulerException ex) {

View File

@@ -16,7 +16,6 @@
package org.springframework.scheduling.quartz;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
@@ -28,6 +27,7 @@ import org.apache.commons.logging.LogFactory;
import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.JobListener;
import org.quartz.ListenerManager;
import org.quartz.ObjectAlreadyExistsException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
@@ -43,7 +43,6 @@ 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,
@@ -52,8 +51,7 @@ import org.springframework.util.ReflectionUtils;
* <p>For concrete usage, check out the {@link SchedulerFactoryBean} and
* {@link SchedulerAccessorBean} classes.
*
* <p>Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0.
* <b>Note:</b> Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+.
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
@@ -61,23 +59,6 @@ import org.springframework.util.ReflectionUtils;
*/
public abstract class SchedulerAccessor implements ResourceLoaderAware {
private static Class<?> jobKeyClass;
private static Class<?> triggerKeyClass;
static {
// Quartz 2.0 job/trigger key available?
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;
@@ -94,24 +75,13 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
private JobListener[] globalJobListeners;
private JobListener[] jobListeners;
private TriggerListener[] globalTriggerListeners;
private TriggerListener[] triggerListeners;
private PlatformTransactionManager transactionManager;
protected ResourceLoader resourceLoader;
public SchedulerAccessor() {
if (jobKeyClass == null && logger.isInfoEnabled()) {
logger.info("Spring's Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+");
}
}
/**
* Set whether any jobs defined on this SchedulerFactoryBean should overwrite
* existing job definitions. Default is "false", to not overwrite already
@@ -151,8 +121,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
* in combination with the Trigger.
* @see #setTriggers
* @see org.quartz.JobDetail
* @see JobDetailBean
* @see JobDetailAwareTrigger
*/
public void setJobDetails(JobDetail... jobDetails) {
// Use modifiable ArrayList here, to allow for further adding of
@@ -180,15 +148,11 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
* "jobDetails" property of this FactoryBean.
* @see #setJobDetails
* @see org.quartz.JobDetail
* @see JobDetailAwareTrigger
* @see CronTriggerBean
* @see SimpleTriggerBean
*/
public void setTriggers(Trigger... triggers) {
this.triggers = Arrays.asList(triggers);
}
/**
* Specify Quartz SchedulerListeners to be registered with the Scheduler.
*/
@@ -204,21 +168,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
this.globalJobListeners = globalJobListeners;
}
/**
* Specify named Quartz JobListeners to be registered with the Scheduler.
* Such JobListeners will only apply to Jobs that explicitly activate
* them via their name.
* <p>Note that non-global JobListeners are not supported on Quartz 2.x -
* manually register a Matcher against the Quartz ListenerManager instead.
* @see org.quartz.JobListener#getName
* @see JobDetailBean#setJobListenerNames
* @deprecated as of Spring 4.0, since it only works on Quartz 1.x
*/
@Deprecated
public void setJobListeners(JobListener... jobListeners) {
this.jobListeners = jobListeners;
}
/**
* Specify global Quartz TriggerListeners to be registered with the Scheduler.
* Such TriggerListeners will apply to all Triggers in the Scheduler.
@@ -227,23 +176,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
this.globalTriggerListeners = globalTriggerListeners;
}
/**
* Specify named Quartz TriggerListeners to be registered with the Scheduler.
* Such TriggerListeners will only apply to Triggers that explicitly activate
* them via their name.
* <p>Note that non-global TriggerListeners are not supported on Quartz 2.x -
* manually register a Matcher against the Quartz ListenerManager instead.
* @see org.quartz.TriggerListener#getName
* @see CronTriggerBean#setTriggerListenerNames
* @see SimpleTriggerBean#setTriggerListenerNames
* @deprecated as of Spring 4.0, since it only works on Quartz 1.x
*/
@Deprecated
public void setTriggerListeners(TriggerListener... triggerListeners) {
this.triggerListeners = triggerListeners;
}
/**
* Set the transaction manager to be used for registering jobs and triggers
* that are defined by this SchedulerFactoryBean. Default is none; setting
@@ -338,7 +270,7 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
* @see #setOverwriteExistingJobs
*/
private boolean addJobToScheduler(JobDetail jobDetail) throws SchedulerException {
if (this.overwriteExistingJobs || !jobDetailExists(jobDetail)) {
if (this.overwriteExistingJobs || getScheduler().getJobDetail(jobDetail.getKey()) == null) {
getScheduler().addJob(jobDetail, true);
return true;
}
@@ -356,10 +288,10 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
* @see #setOverwriteExistingJobs
*/
private boolean addTriggerToScheduler(Trigger trigger) throws SchedulerException {
boolean triggerExists = triggerExists(trigger);
boolean triggerExists = (getScheduler().getTrigger(trigger.getKey()) != null);
if (!triggerExists || this.overwriteExistingJobs) {
// Check if the Trigger is aware of an associated JobDetail.
JobDetail jobDetail = findJobDetail(trigger);
JobDetail jobDetail = (JobDetail) trigger.getJobDataMap().remove("jobDetail");
if (jobDetail != null) {
// Automatically register the JobDetail too.
if (!this.jobDetails.contains(jobDetail) && addJobToScheduler(jobDetail)) {
@@ -376,12 +308,12 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware {
ex.getMessage() + " - can safely be ignored");
}
if (this.overwriteExistingJobs) {
rescheduleJob(trigger);
getScheduler().rescheduleJob(trigger.getKey(), trigger);
}
}
}
else {
rescheduleJob(trigger);
getScheduler().rescheduleJob(trigger.getKey(), trigger);
}
return true;
}
@@ -390,160 +322,25 @@ 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.remove(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 {
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 {
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;
}
Class<?> targetClass = target.getClass();
try {
if (this.schedulerListeners != null) {
Method addSchedulerListener = targetClass.getMethod("addSchedulerListener", SchedulerListener.class);
for (SchedulerListener listener : this.schedulerListeners) {
ReflectionUtils.invokeMethod(addSchedulerListener, target, listener);
}
}
if (this.globalJobListeners != null) {
Method addJobListener;
if (quartz2) {
// addJobListener(JobListener) only introduced as late as Quartz 2.2, so we need
// to fall back to the Quartz 2.0/2.1 compatible variant with an empty matchers List
addJobListener = targetClass.getMethod("addJobListener", JobListener.class, List.class);
}
else {
addJobListener = targetClass.getMethod("addGlobalJobListener", JobListener.class);
}
for (JobListener listener : this.globalJobListeners) {
if (quartz2) {
List<?> emptyMatchers = new LinkedList<Object>();
ReflectionUtils.invokeMethod(addJobListener, target, listener, emptyMatchers);
}
else {
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;
if (quartz2) {
// addTriggerListener(TriggerListener) only introduced as late as Quartz 2.2, so we need
// to fall back to the Quartz 2.0/2.1 compatible variant with an empty matchers List
addTriggerListener = targetClass.getMethod("addTriggerListener", TriggerListener.class, List.class);
}
else {
addTriggerListener = targetClass.getMethod("addGlobalTriggerListener", TriggerListener.class);
}
for (TriggerListener listener : this.globalTriggerListeners) {
if (quartz2) {
List<?> emptyMatchers = new LinkedList<Object>();
ReflectionUtils.invokeMethod(addTriggerListener, target, listener, emptyMatchers);
}
else {
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);
}
ListenerManager listenerManager = getScheduler().getListenerManager();
if (this.schedulerListeners != null) {
for (SchedulerListener listener : this.schedulerListeners) {
listenerManager.addSchedulerListener(listener);
}
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("Expected Quartz API not present: " + ex);
if (this.globalJobListeners != null) {
for (JobListener listener : this.globalJobListeners) {
listenerManager.addJobListener(listener);
}
}
if (this.globalTriggerListeners != null) {
for (TriggerListener listener : this.globalTriggerListeners) {
listenerManager.addTriggerListener(listener);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,8 +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.
*
* <p>Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0.
* <b>Note:</b> Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+.
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.5.6

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -75,8 +75,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.
*
* <p>Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0.
* <b>Note:</b> Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+.
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 18.02.2004

View File

@@ -1,176 +0,0 @@
/*
* Copyright 2002-2013 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.Date;
import java.util.Map;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Constants;
/**
* Convenience subclass of Quartz's {@link org.quartz.SimpleTrigger} class,
* making bean-style usage easier.
*
* <p>{@code 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.
*
* <p>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.
*
* <p><b>NOTE: This convenience subclass does not work against Quartz 2.0.</b>
* Use Quartz 2.0's native {@code 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.x.
*
* @author Juergen Hoeller
* @since 18.02.2004
* @see #setName
* @see #setGroup
* @see #setStartTime
* @see #setJobName
* @see #setJobGroup
* @see #setJobDetail
* @see SchedulerFactoryBean#setTriggers
* @see SchedulerFactoryBean#setJobDetails
* @see CronTriggerBean
*/
@SuppressWarnings("serial")
public class SimpleTriggerBean extends SimpleTrigger
implements JobDetailAwareTrigger, BeanNameAware, InitializingBean {
/** Constants for the SimpleTrigger class */
private static final Constants constants = new Constants(SimpleTrigger.class);
private long startDelay = 0;
private JobDetail jobDetail;
private String beanName;
public SimpleTriggerBean() {
setRepeatCount(REPEAT_INDEFINITELY);
}
/**
* Register objects in the JobDataMap via a given Map.
* <p>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 JobDetailBean#setJobDataAsMap
*/
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
getJobDataMap().putAll(jobDataAsMap);
}
/**
* Set the misfire instruction via the name of the corresponding
* constant in the {@link org.quartz.SimpleTrigger} class.
* Default is {@code 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) {
setMisfireInstruction(constants.asNumber(constantName).intValue());
}
/**
* Set a list of TriggerListener names for this job, referring to
* non-global TriggerListeners registered with the Scheduler.
* <p>A TriggerListener name always refers to the name returned
* by the TriggerListener implementation.
* @see SchedulerFactoryBean#setTriggerListeners
* @see org.quartz.TriggerListener#getName
* @deprecated as of Spring 4.0, since it only works on Quartz 1.x
*/
@Deprecated
public void setTriggerListenerNames(String... names) {
for (String name : names) {
addTriggerListener(name);
}
}
/**
* Set the start delay in milliseconds.
* <p>The start delay is added to the current system time (when the bean starts)
* to control the {@link #setStartTime start time} of the trigger.
* <p>If the start delay is non-zero, it will <strong>always</strong>
* take precedence over start time.
* @param startDelay the start delay, in milliseconds
*/
public void setStartDelay(long startDelay) {
this.startDelay = startDelay;
}
/**
* Set the JobDetail that this trigger should be associated with.
* <p>This is typically used with a bean reference if the JobDetail
* is a Spring-managed bean. Alternatively, the trigger can also
* be associated with a job by name and group.
* @see #setJobName
* @see #setJobGroup
*/
public void setJobDetail(JobDetail jobDetail) {
this.jobDetail = jobDetail;
}
@Override
public JobDetail getJobDetail() {
return this.jobDetail;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void afterPropertiesSet() {
if (getName() == null) {
setName(this.beanName);
}
if (getGroup() == null) {
setGroup(Scheduler.DEFAULT_GROUP);
}
if (this.startDelay > 0 || getStartTime() == null) {
setStartTime(new Date(System.currentTimeMillis() + this.startDelay));
}
if (this.jobDetail != null) {
setJobName(this.jobDetail.getName());
setJobGroup(this.jobDetail.getGroup());
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,7 +16,6 @@
package org.springframework.scheduling.quartz;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
@@ -24,16 +23,13 @@ import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;
import org.quartz.impl.triggers.SimpleTriggerImpl;
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}
@@ -48,9 +44,6 @@ import org.springframework.util.ReflectionUtils;
* to automatically register a trigger for the corresponding JobDetail,
* instead of registering the JobDetail separately.
*
* <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.x,
* in contrast to the older {@link SimpleTriggerBean} class.
*
* @author Juergen Hoeller
* @since 3.1
* @see #setName
@@ -59,7 +52,6 @@ import org.springframework.util.ReflectionUtils;
* @see #setJobDetail
* @see SchedulerFactoryBean#setTriggers
* @see SchedulerFactoryBean#setJobDetails
* @see CronTriggerBean
*/
public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, BeanNameAware, InitializingBean {
@@ -136,7 +128,6 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
* 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<String, ?> jobDataAsMap) {
this.jobDataMap.putAll(jobDataAsMap);
@@ -228,13 +219,12 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
this.group = Scheduler.DEFAULT_GROUP;
}
if (this.jobDetail != null) {
this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
this.jobDataMap.put("jobDetail", this.jobDetail);
}
if (this.startDelay > 0 || this.startTime == null) {
this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
}
/*
SimpleTriggerImpl sti = new SimpleTriggerImpl();
sti.setName(this.name);
sti.setGroup(this.group);
@@ -245,43 +235,8 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
sti.setRepeatCount(this.repeatCount);
sti.setPriority(this.priority);
sti.setMisfireInstruction(this.misfireInstruction);
cti.setDescription(this.description);
sti.setDescription(this.description);
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", this.repeatCount);
pvs.add("priority", this.priority);
pvs.add("misfireInstruction", this.misfireInstruction);
pvs.add("description", this.description);
bw.setPropertyValues(pvs);
this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,29 +16,24 @@
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
* dependency injection on bean properties. This is essentially the direct
* equivalent of Spring's {@link QuartzJobBean} in the shape of a
* Quartz 1.5 {@link org.quartz.spi.JobFactory}.
* equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz
* {@link org.quartz.spi.JobFactory}.
*
* <p>Applies scheduler context, job data map and trigger data map entries
* 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.
*
* <p>Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0.
* <b>Note:</b> Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+.
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.0
@@ -83,8 +78,8 @@ public class SpringBeanJobFactory extends AdaptableJobFactory implements Schedul
if (this.schedulerContext != null) {
pvs.addPropertyValues(this.schedulerContext);
}
pvs.addPropertyValues(getJobDetailDataMap(bundle));
pvs.addPropertyValues(getTriggerDataMap(bundle));
pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
if (this.ignoredUnknownProperties != null) {
for (String propName : this.ignoredUnknownProperties) {
if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
@@ -112,20 +107,4 @@ 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);
}
}