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. * <p>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. * <p>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. * <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; } 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 given <code>Job</code> instance. * * Quartz does not store an actual instance of a <code>Job</code> class, but * instead allows you to define an instance of one, through the use of a <code>JobDetail</code>. * </p> * * <p> * <code>Job</code> s have a name and group associated with them, which * should uniquely identify them within a single <code>{@link Scheduler}</code>. * </p> * * <p> * <code>Trigger</code> s are the 'mechanism' by which <code>Job</code> s * are scheduled. Many <code>Trigger</code> s can point to the same <code>Job</code>, * but a single <code>Trigger</code> can only point to one <code>Job</code>. * </p> */ 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 a <code>JobDetail</code> with no specified name or group, and * the default settings of all the other properties. * * <p> * 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} * </p> */ public JobDetail() { // do nothing... } /** * Create a <code>JobDetail</code> with the given name, and group, and * the default settings of all the other properties. * * @param group if <code>null</code>, 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 a <code>JobDetail</code> with the given name, and group, and * the given settings of all the other properties. * * @param group if <code>null</code>, 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 this <code>Job</code>. */ public String getName() { return name; } /** * Set the name of this <code>Job</code>. * * @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 this <code>Job</code>. */ public String getGroup() { return group; } /** * Set the group of this <code>Job</code>. * * @param group if <code>null</code>, 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 the <code>Trigger</code> in the format * "group.name". */ public String getFullName() { return group + "." + name; } /** * Return the description given to the <code>Job</code> instance by its * creator (if any). * @return null if no description was set. */ public String getDescription() { return description; } /** * Set a description for the <code>Job</code> 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 of <code>Job</code> that will be executed. */ public Class getJobClass() { return jobClass; } /** * Set the instance of <code>Job</code> that will be executed. * * @exception IllegalArgumentException * if jobClass is null or the class is not a <code>Job</code>. */ 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 the <code>JobDataMap</code> that is associated with the <code>Job</code>. */ public JobDataMap getJobDataMap() { if (jobDataMap == null) jobDataMap = new JobDataMap(); return jobDataMap; } /** * Set the <code>JobDataMap</code> to be associated with the <code>Job</code>. */ public void setJobDataMap(JobDataMap jobDataMap) { this.jobDataMap = jobDataMap; } /** * Validates whether the properties of the <code>JobDetail</code> are * valid for submission into a <code>Scheduler</code>. * * @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 the <code>Job</code> should be persisted in the * <code>{@link org.quartz.spi.JobStore}</code> for re-use after program * restarts. * * <p> * If not explicitly set, the default value is <code>false</code>. * </p> */ public void setVolatility(boolean volatility) { this.volatility = volatility; } /** * Set whether or not the <code>Job</code> should remain stored after it * is orphaned (no <code>{@link Trigger}s</code> point to it). * * If not explicitly set, the default value is <code>false</code>. */ public void setDurability(boolean durability) { this.durability = durability; } /** * Set whether or not the the <code>Scheduler</code> should re-execute * the <code>Job</code> if a 'recovery' or 'fail-over' situation is * encountered. * * If not explicitly set, the default value is <code>false</code>. * * @see JobExecutionContext#isRecovering() * @see JobExecutionContext#isFailedOver() */ public void setRequestsRecovery(boolean shouldRecover) { this.shouldRecover = shouldRecover; } /** * Whether or not the <code>Job</code> should not be persisted in the * <code>{@link org.quartz.spi.JobStore}</code> for re-use after program * restarts. * * If not explicitly set, the default value is <code>false</code>. * * @return <code>true</code> if the <code>Job</code> should be garbage * collected along with the <code>{@link Scheduler}</code>. */ public boolean isVolatile() { return volatility; } /** * Whether or not the <code>Job</code> should remain stored after it is * orphaned (no <code>{@link Trigger}s</code> point to it). * * If not explicitly set, the default value is <code>false</code>. * * @return <code>true</code> if the Job should remain persisted after * being orphaned. */ public boolean isDurable() { return durability; } /** * Whether or not the <code>Job</code> implements the interface <code>{@link StatefulJob}</code>. */ public boolean isStateful() { if (jobClass == null) return false; return (StatefulJob.class.isAssignableFrom(jobClass)); } /** * Instructs the <code>Scheduler</code> whether or not the <code>Job</code> * should be re-executed if a 'recovery' or 'fail-over' situation is * encountered. * * If not explicitly set, the default value is <code>false</code>. * * @see JobExecutionContext#isRecovering() * @see JobExecutionContext#isFailedOver() */ public boolean requestsRecovery() { return shouldRecover; } /** * Add the specified name of a <code>{@link JobListener}</code> to the * end of the <code>Job</code>'s list of listeners. */ public void addJobListener(String name) { jobListeners.add(name); } /** * Remove the specified name of a <code>{@link JobListener}</code> from * the <code>Job</code>'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 of <code>String</code> s containing the names of all * <code>{@link JobListener}</code> s assigned to the <code>Job</code>, * 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; } }3. 说明