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' JobDetail class that eases bean-style * usage. * * 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. */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 */ 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. */ public Class getJobClass() { return (this.actualJobClass != null ? this.actualJobClass : super.getJobClass()); } /** * Register objects in the JobDataMap via a given Map. *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. */ public void setJobDataAsMap(Map jobDataAsMap) { getJobDataMap().putAll(jobDataAsMap); } /** * Set a list of JobListener names for this job, referring to * non-global JobListeners registered with the Scheduler. *
A JobListener name always refers to the name returned * by the JobListener implementation. */ public void setJobListenerNames(String[] names) { for (int i = 0; i < names.length; i++) { addJobListener(names[i]); } } public void setBeanName(String beanName) { this.beanName = beanName; } 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. *
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 SchedulerFactoryBean#setApplicationContextSchedulerContextKey * @see org.springframework.context.ApplicationContext */ public void setApplicationContextJobDataKey(String applicationContextJobDataKey) { this.applicationContextJobDataKey = applicationContextJobDataKey; } 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); } } }
JobDetail.java
package org.quartz; import java.util.ArrayList; /** * Conveys the detail properties of a given3. 说明 <1> 常用的构造方法-----public JobDetail(String name, String group, Class jobClass) <2> 比较重要的属性JobDataMap----可以传递参数。Job
instance. * * Quartz does not store an actual instance of aJob
class, but * instead allows you to define an instance of one, through the use of aJobDetail
. * * **
* *Job
s have a name and group associated with them, which * should uniquely identify them within a single{@link Scheduler}
. **
*/public class JobDetail implements Cloneable, java.io.Serializable { private String name; private String group = Scheduler.DEFAULT_GROUP; private String description; private Class jobClass; private JobDataMap jobDataMap; private boolean volatility = false; private boolean durability = false; private boolean shouldRecover = false; private ArrayList jobListeners = new ArrayList(2); /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Constructors. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * Create aTrigger
s are the 'mechanism' by whichJob
s * are scheduled. ManyTrigger
s can point to the sameJob
, * but a singleTrigger
can only point to oneJob
. *JobDetail
with no specified name or group, and * the default settings of all the other properties. * ** Note that the {@link #setName(String)},{@link #setGroup(String)}and * {@link #setJobClass(Class)}methods must be called before the job can be * placed into a {@link Scheduler} *
*/ public JobDetail() { // do nothing... } /** * Create aJobDetail
with the given name, and group, and * the default settings of all the other properties. * * @param group ifnull
, Scheduler.DEFAULT_GROUP will be used. * * @exception IllegalArgumentException * if nameis null or empty, or the group is an empty string. */ public JobDetail(String name, String group, Class jobClass) { setName(name); setGroup(group); setJobClass(jobClass); } /** * Create aJobDetail
with the given name, and group, and * the given settings of all the other properties. * * @param group ifnull
, Scheduler.DEFAULT_GROUP will be used. * * @exception IllegalArgumentException * if nameis null or empty, or the group is an empty string. */ public JobDetail(String name, String group, Class jobClass, boolean volatility, boolean durability, boolean recover) { setName(name); setGroup(group); setJobClass(jobClass); setVolatility(volatility); setDurability(durability); setRequestsRecovery(recover); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Interface. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * Get the name of thisJob
. */ public String getName() { return name; } /** * Set the name of thisJob
. * * @exception IllegalArgumentException * if name is null or empty. */ public void setName(String name) { if (name == null || name.trim().length() == 0) throw new IllegalArgumentException("Job name cannot be empty."); this.name = name; } /** * Get the group of thisJob
. */ public String getGroup() { return group; } /** * Set the group of thisJob
. * * @param group ifnull
, Scheduler.DEFAULT_GROUP will be used. * @exception IllegalArgumentException * if the group is an empty string. */ public void setGroup(String group) { if (group != null && group.trim().length() == 0) throw new IllegalArgumentException( "Group name cannot be empty."); if(group == null) group = Scheduler.DEFAULT_GROUP; this.group = group; } /** * Returns the 'full name' of theTrigger
in the format * "group.name". */ public String getFullName() { return group + "." + name; } /** * Return the description given to theJob
instance by its * creator (if any). * @return null if no description was set. */ public String getDescription() { return description; } /** * Set a description for theJob
instance - may be useful * for remembering/displaying the purpose of the job, though the * description has no meaning to Quartz. */ public void setDescription(String description) { this.description = description; } /** * Get the instance ofJob
that will be executed. */ public Class getJobClass() { return jobClass; } /** * Set the instance ofJob
that will be executed. * * @exception IllegalArgumentException * if jobClass is null or the class is not aJob
. */ public void setJobClass(Class jobClass) { if (jobClass == null) throw new IllegalArgumentException("Job class cannot be null."); if (!Job.class.isAssignableFrom(jobClass)) throw new IllegalArgumentException( "Job class must implement the Job interface."); this.jobClass = jobClass; } /** * Get theJobDataMap
that is associated with theJob
. */ public JobDataMap getJobDataMap() { if (jobDataMap == null) jobDataMap = new JobDataMap(); return jobDataMap; } /** * Set theJobDataMap
to be associated with theJob
. */ public void setJobDataMap(JobDataMap jobDataMap) { this.jobDataMap = jobDataMap; } /** * Validates whether the properties of theJobDetail
are * valid for submission into aScheduler
. * * @throws IllegalStateException * if a required property (such as Name, Group, Class) is not * set. */ public void validate() throws SchedulerException { if (name == null) throw new SchedulerException("Job's name cannot be null", SchedulerException.ERR_CLIENT_ERROR); if (group == null) throw new SchedulerException("Job's group cannot be null", SchedulerException.ERR_CLIENT_ERROR); if (jobClass == null) throw new SchedulerException("Job's class cannot be null", SchedulerException.ERR_CLIENT_ERROR); } /** * Set whether or not theJob
should be persisted in the *{@link org.quartz.spi.JobStore}
for re-use after program * restarts. * ** If not explicitly set, the default value is
*/ public void setVolatility(boolean volatility) { this.volatility = volatility; } /** * Set whether or not thefalse
. *Job
should remain stored after it * is orphaned (no{@link Trigger}s
point to it). * * If not explicitly set, the default value isfalse
. */ public void setDurability(boolean durability) { this.durability = durability; } /** * Set whether or not the theScheduler
should re-execute * theJob
if a 'recovery' or 'fail-over' situation is * encountered. * * If not explicitly set, the default value isfalse
. * * @see JobExecutionContext#isRecovering() * @see JobExecutionContext#isFailedOver() */ public void setRequestsRecovery(boolean shouldRecover) { this.shouldRecover = shouldRecover; } /** * Whether or not theJob
should not be persisted in the *{@link org.quartz.spi.JobStore}
for re-use after program * restarts. * * If not explicitly set, the default value isfalse
. * * @returntrue
if theJob
should be garbage * collected along with the{@link Scheduler}
. */ public boolean isVolatile() { return volatility; } /** * Whether or not theJob
should remain stored after it is * orphaned (no{@link Trigger}s
point to it). * * If not explicitly set, the default value isfalse
. * * @returntrue
if the Job should remain persisted after * being orphaned. */ public boolean isDurable() { return durability; } /** * Whether or not theJob
implements the interface{@link StatefulJob}
. */ public boolean isStateful() { if (jobClass == null) return false; return (StatefulJob.class.isAssignableFrom(jobClass)); } /** * Instructs theScheduler
whether or not theJob
* should be re-executed if a 'recovery' or 'fail-over' situation is * encountered. * * If not explicitly set, the default value isfalse
. * * @see JobExecutionContext#isRecovering() * @see JobExecutionContext#isFailedOver() */ public boolean requestsRecovery() { return shouldRecover; } /** * Add the specified name of a{@link JobListener}
to the * end of theJob
's list of listeners. */ public void addJobListener(String name) { jobListeners.add(name); } /** * Remove the specified name of a{@link JobListener}
from * theJob
's list of listeners. * * @return true if the given name was found in the list, and removed */ public boolean removeJobListener(String name) { return jobListeners.remove(name); } /** * Returns an array ofString
s containing the names of all *{@link JobListener}
s assigned to theJob
, * in the order in which they should be notified. */ public String[] getJobListenerNames() { return (String[]) jobListeners.toArray(new String[jobListeners.size()]); } /** * Return a simple string representation of this object. */ public String toString() { return "JobDetail '" + getFullName() + "': jobClass: '" + ((getJobClass() == null) ? null : getJobClass().getName()) + " isStateful: " + isStateful() + " isVolatile: " + isVolatile() + " isDurable: " + isDurable() + " requestsRecovers: " + requestsRecovery(); } public Object clone() { JobDetail copy; try { copy = (JobDetail) super.clone(); copy.jobListeners = (ArrayList) jobListeners.clone(); if (jobDataMap != null) copy.jobDataMap = (JobDataMap) jobDataMap.clone(); } catch (CloneNotSupportedException ex) { throw new IncompatibleClassChangeError("Not Cloneable."); } return copy; } }