1 /* 2 * Copyright 2002-2012 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.scheduling.quartz; 18 19 import java.lang.reflect.Method; 20 import java.text.ParseException; 21 import java.util.Date; 22 import java.util.Map; 23 24 import org.quartz.JobDataMap; 25 import org.quartz.JobDetail; 26 import org.quartz.Scheduler; 27 import org.quartz.SimpleTrigger; 28 29 import org.springframework.beans.BeanWrapper; 30 import org.springframework.beans.BeanWrapperImpl; 31 import org.springframework.beans.MutablePropertyValues; 32 import org.springframework.beans.factory.BeanNameAware; 33 import org.springframework.beans.factory.FactoryBean; 34 import org.springframework.beans.factory.InitializingBean; 35 import org.springframework.core.Constants; 36 import org.springframework.util.Assert; 37 import org.springframework.util.ReflectionUtils; 38 39 /** 40 * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.SimpleTrigger} 41 * instance, supporting bean-style usage for trigger configuration. 42 * 43 * <p><code>SimpleTrigger(Impl)</code> itself is already a JavaBean but lacks sensible defaults. 44 * This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT") 45 * as job group, the current time as start time, and indefinite repetition, if not specified. 46 * 47 * <p>This class will also register the trigger with the job name and group of 48 * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean} 49 * to automatically register a trigger for the corresponding JobDetail, 50 * instead of registering the JobDetail separately. 51 * 52 * <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1, 53 * in contrast to the older {@link SimpleTriggerBean} class. 54 * 55 * @author Juergen Hoeller 56 * @since 3.1 57 * @see #setName 58 * @see #setGroup 59 * @see #setStartDelay 60 * @see #setJobDetail 61 * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers 62 * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails 63 * @see org.springframework.scheduling.quartz.CronTriggerBean 64 */ 65 public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, BeanNameAware, InitializingBean { 66 67 /** Constants for the SimpleTrigger class */ 68 private static final Constants constants = new Constants(SimpleTrigger.class); 69 70 71 private String name; 72 73 private String group; 74 75 private JobDetail jobDetail; 76 77 private JobDataMap jobDataMap = new JobDataMap(); 78 79 private Date startTime; 80 81 private long startDelay; 82 83 private long repeatInterval; 84 85 private int repeatCount = -1; 86 87 private int priority; 88 89 private int misfireInstruction; 90 91 private String beanName; 92 93 private SimpleTrigger simpleTrigger; 94 95 96 /** 97 * Specify the trigger's name. 98 */ setName(String name)99 public void setName(String name) { 100 this.name = name; 101 } 102 103 /** 104 * Specify the trigger's group. 105 */ setGroup(String group)106 public void setGroup(String group) { 107 this.group = group; 108 } 109 110 /** 111 * Set the JobDetail that this trigger should be associated with. 112 */ setJobDetail(JobDetail jobDetail)113 public void setJobDetail(JobDetail jobDetail) { 114 this.jobDetail = jobDetail; 115 } 116 117 /** 118 * Set the trigger's JobDataMap. 119 * @see #setJobDataAsMap 120 */ setJobDataMap(JobDataMap jobDataMap)121 public void setJobDataMap(JobDataMap jobDataMap) { 122 this.jobDataMap = jobDataMap; 123 } 124 125 /** 126 * Return the trigger's JobDataMap. 127 */ getJobDataMap()128 public JobDataMap getJobDataMap() { 129 return this.jobDataMap; 130 } 131 132 /** 133 * Register objects in the JobDataMap via a given Map. 134 * <p>These objects will be available to this Trigger only, 135 * in contrast to objects in the JobDetail's data map. 136 * @param jobDataAsMap Map with String keys and any objects as values 137 * (for example Spring-managed beans) 138 * @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap 139 */ setJobDataAsMap(Map<String, ?> jobDataAsMap)140 public void setJobDataAsMap(Map<String, ?> jobDataAsMap) { 141 this.jobDataMap.putAll(jobDataAsMap); 142 } 143 144 /** 145 * Set the start delay in milliseconds. 146 * <p>The start delay is added to the current system time (when the bean starts) 147 * to control the start time of the trigger. 148 */ setStartDelay(long startDelay)149 public void setStartDelay(long startDelay) { 150 Assert.isTrue(startDelay >= 0, "Start delay cannot be negative"); 151 this.startDelay = startDelay; 152 } 153 154 /** 155 * Specify the interval between execution times of this trigger. 156 */ setRepeatInterval(long repeatInterval)157 public void setRepeatInterval(long repeatInterval) { 158 this.repeatInterval = repeatInterval; 159 } 160 161 /** 162 * Specify the number of times this trigger is supposed to fire. 163 * <p>Default is to repeat indefinitely. 164 */ setRepeatCount(int repeatCount)165 public void setRepeatCount(int repeatCount) { 166 this.repeatCount = repeatCount; 167 } 168 169 /** 170 * Specify the priority of this trigger. 171 */ setPriority(int priority)172 public void setPriority(int priority) { 173 this.priority = priority; 174 } 175 176 /** 177 * Specify a misfire instruction for this trigger. 178 */ setMisfireInstruction(int misfireInstruction)179 public void setMisfireInstruction(int misfireInstruction) { 180 this.misfireInstruction = misfireInstruction; 181 } 182 183 /** 184 * Set the misfire instruction via the name of the corresponding 185 * constant in the {@link org.quartz.SimpleTrigger} class. 186 * Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>. 187 * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW 188 * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT 189 * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT 190 * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT 191 * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT 192 * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY 193 */ setMisfireInstructionName(String constantName)194 public void setMisfireInstructionName(String constantName) { 195 this.misfireInstruction = constants.asNumber(constantName).intValue(); 196 } 197 setBeanName(String beanName)198 public void setBeanName(String beanName) { 199 this.beanName = beanName; 200 } 201 202 afterPropertiesSet()203 public void afterPropertiesSet() throws ParseException { 204 if (this.name == null) { 205 this.name = this.beanName; 206 } 207 if (this.group == null) { 208 this.group = Scheduler.DEFAULT_GROUP; 209 } 210 if (this.jobDetail != null) { 211 this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail); 212 } 213 if (this.startDelay > 0) { 214 this.startTime = new Date(System.currentTimeMillis() + this.startDelay); 215 } 216 else if (this.startTime == null) { 217 this.startTime = new Date(); 218 } 219 220 /* 221 SimpleTriggerImpl sti = new SimpleTriggerImpl(); 222 sti.setName(this.name); 223 sti.setGroup(this.group); 224 sti.setJobKey(this.jobDetail.getKey()); 225 sti.setJobDataMap(this.jobDataMap); 226 sti.setStartTime(this.startTime); 227 sti.setRepeatInterval(this.repeatInterval); 228 sti.setRepeatCount(this.repeatCount); 229 sti.setPriority(this.priority); 230 sti.setMisfireInstruction(this.misfireInstruction); 231 this.simpleTrigger = sti; 232 */ 233 234 Class simpleTriggerClass; 235 Method jobKeyMethod; 236 try { 237 simpleTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.SimpleTriggerImpl"); 238 jobKeyMethod = JobDetail.class.getMethod("getKey"); 239 } 240 catch (ClassNotFoundException ex) { 241 simpleTriggerClass = SimpleTrigger.class; 242 jobKeyMethod = null; 243 } 244 catch (NoSuchMethodException ex) { 245 throw new IllegalStateException("Incompatible Quartz version"); 246 } 247 BeanWrapper bw = new BeanWrapperImpl(simpleTriggerClass); 248 MutablePropertyValues pvs = new MutablePropertyValues(); 249 pvs.add("name", this.name); 250 pvs.add("group", this.group); 251 if (jobKeyMethod != null) { 252 pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail)); 253 } 254 else { 255 pvs.add("jobName", this.jobDetail.getName()); 256 pvs.add("jobGroup", this.jobDetail.getGroup()); 257 } 258 pvs.add("jobDataMap", this.jobDataMap); 259 pvs.add("startTime", this.startTime); 260 pvs.add("repeatInterval", this.repeatInterval); 261 pvs.add("repeatCount", this.repeatCount); 262 pvs.add("priority", this.priority); 263 pvs.add("misfireInstruction", this.misfireInstruction); 264 bw.setPropertyValues(pvs); 265 this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance(); 266 } 267 268 getObject()269 public SimpleTrigger getObject() { 270 return this.simpleTrigger; 271 } 272 getObjectType()273 public Class<?> getObjectType() { 274 return SimpleTrigger.class; 275 } 276 isSingleton()277 public boolean isSingleton() { 278 return true; 279 } 280 } 281