001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.scheduling.quartz; 018 019import java.text.ParseException; 020import java.util.Date; 021import java.util.Map; 022import java.util.TimeZone; 023 024import org.quartz.CronTrigger; 025import org.quartz.JobDataMap; 026import org.quartz.JobDetail; 027import org.quartz.Scheduler; 028import org.quartz.impl.triggers.CronTriggerImpl; 029 030import org.springframework.beans.factory.BeanNameAware; 031import org.springframework.beans.factory.FactoryBean; 032import org.springframework.beans.factory.InitializingBean; 033import org.springframework.core.Constants; 034import org.springframework.lang.Nullable; 035import org.springframework.util.Assert; 036 037/** 038 * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.CronTrigger} 039 * instance, supporting bean-style usage for trigger configuration. 040 * 041 * <p>{@code CronTrigger(Impl)} itself is already a JavaBean but lacks sensible defaults. 042 * This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT") 043 * as job group, the current time as start time, and indefinite repetition, if not specified. 044 * 045 * <p>This class will also register the trigger with the job name and group of 046 * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean} 047 * to automatically register a trigger for the corresponding JobDetail, 048 * instead of registering the JobDetail separately. 049 * 050 * @author Juergen Hoeller 051 * @since 3.1 052 * @see #setName 053 * @see #setGroup 054 * @see #setStartDelay 055 * @see #setJobDetail 056 * @see SchedulerFactoryBean#setTriggers 057 * @see SchedulerFactoryBean#setJobDetails 058 */ 059public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNameAware, InitializingBean { 060 061 /** Constants for the CronTrigger class. */ 062 private static final Constants constants = new Constants(CronTrigger.class); 063 064 065 @Nullable 066 private String name; 067 068 @Nullable 069 private String group; 070 071 @Nullable 072 private JobDetail jobDetail; 073 074 private JobDataMap jobDataMap = new JobDataMap(); 075 076 @Nullable 077 private Date startTime; 078 079 private long startDelay = 0; 080 081 @Nullable 082 private String cronExpression; 083 084 @Nullable 085 private TimeZone timeZone; 086 087 @Nullable 088 private String calendarName; 089 090 private int priority; 091 092 private int misfireInstruction; 093 094 @Nullable 095 private String description; 096 097 @Nullable 098 private String beanName; 099 100 @Nullable 101 private CronTrigger cronTrigger; 102 103 104 /** 105 * Specify the trigger's name. 106 */ 107 public void setName(String name) { 108 this.name = name; 109 } 110 111 /** 112 * Specify the trigger's group. 113 */ 114 public void setGroup(String group) { 115 this.group = group; 116 } 117 118 /** 119 * Set the JobDetail that this trigger should be associated with. 120 */ 121 public void setJobDetail(JobDetail jobDetail) { 122 this.jobDetail = jobDetail; 123 } 124 125 /** 126 * Set the trigger's JobDataMap. 127 * @see #setJobDataAsMap 128 */ 129 public void setJobDataMap(JobDataMap jobDataMap) { 130 this.jobDataMap = jobDataMap; 131 } 132 133 /** 134 * Return the trigger's JobDataMap. 135 */ 136 public JobDataMap getJobDataMap() { 137 return this.jobDataMap; 138 } 139 140 /** 141 * Register objects in the JobDataMap via a given Map. 142 * <p>These objects will be available to this Trigger only, 143 * in contrast to objects in the JobDetail's data map. 144 * @param jobDataAsMap a Map with String keys and any objects as values 145 * (for example Spring-managed beans) 146 */ 147 public void setJobDataAsMap(Map<String, ?> jobDataAsMap) { 148 this.jobDataMap.putAll(jobDataAsMap); 149 } 150 151 /** 152 * Set a specific start time for the trigger. 153 * <p>Note that a dynamically computed {@link #setStartDelay} specification 154 * overrides a static timestamp set here. 155 */ 156 public void setStartTime(Date startTime) { 157 this.startTime = startTime; 158 } 159 160 /** 161 * Set the start delay in milliseconds. 162 * <p>The start delay is added to the current system time (when the bean starts) 163 * to control the start time of the trigger. 164 */ 165 public void setStartDelay(long startDelay) { 166 Assert.isTrue(startDelay >= 0, "Start delay cannot be negative"); 167 this.startDelay = startDelay; 168 } 169 170 /** 171 * Specify the cron expression for this trigger. 172 */ 173 public void setCronExpression(String cronExpression) { 174 this.cronExpression = cronExpression; 175 } 176 177 /** 178 * Specify the time zone for this trigger's cron expression. 179 */ 180 public void setTimeZone(TimeZone timeZone) { 181 this.timeZone = timeZone; 182 } 183 184 /** 185 * Associate a specific calendar with this cron trigger. 186 */ 187 public void setCalendarName(String calendarName) { 188 this.calendarName = calendarName; 189 } 190 191 /** 192 * Specify the priority of this trigger. 193 */ 194 public void setPriority(int priority) { 195 this.priority = priority; 196 } 197 198 /** 199 * Specify a misfire instruction for this trigger. 200 */ 201 public void setMisfireInstruction(int misfireInstruction) { 202 this.misfireInstruction = misfireInstruction; 203 } 204 205 /** 206 * Set the misfire instruction via the name of the corresponding 207 * constant in the {@link org.quartz.CronTrigger} class. 208 * Default is {@code MISFIRE_INSTRUCTION_SMART_POLICY}. 209 * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 210 * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING 211 * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY 212 */ 213 public void setMisfireInstructionName(String constantName) { 214 this.misfireInstruction = constants.asNumber(constantName).intValue(); 215 } 216 217 /** 218 * Associate a textual description with this trigger. 219 */ 220 public void setDescription(String description) { 221 this.description = description; 222 } 223 224 @Override 225 public void setBeanName(String beanName) { 226 this.beanName = beanName; 227 } 228 229 230 @Override 231 public void afterPropertiesSet() throws ParseException { 232 Assert.notNull(this.cronExpression, "Property 'cronExpression' is required"); 233 234 if (this.name == null) { 235 this.name = this.beanName; 236 } 237 if (this.group == null) { 238 this.group = Scheduler.DEFAULT_GROUP; 239 } 240 if (this.jobDetail != null) { 241 this.jobDataMap.put("jobDetail", this.jobDetail); 242 } 243 if (this.startDelay > 0 || this.startTime == null) { 244 this.startTime = new Date(System.currentTimeMillis() + this.startDelay); 245 } 246 if (this.timeZone == null) { 247 this.timeZone = TimeZone.getDefault(); 248 } 249 250 CronTriggerImpl cti = new CronTriggerImpl(); 251 cti.setName(this.name != null ? this.name : toString()); 252 cti.setGroup(this.group); 253 if (this.jobDetail != null) { 254 cti.setJobKey(this.jobDetail.getKey()); 255 } 256 cti.setJobDataMap(this.jobDataMap); 257 cti.setStartTime(this.startTime); 258 cti.setCronExpression(this.cronExpression); 259 cti.setTimeZone(this.timeZone); 260 cti.setCalendarName(this.calendarName); 261 cti.setPriority(this.priority); 262 cti.setMisfireInstruction(this.misfireInstruction); 263 cti.setDescription(this.description); 264 this.cronTrigger = cti; 265 } 266 267 268 @Override 269 @Nullable 270 public CronTrigger getObject() { 271 return this.cronTrigger; 272 } 273 274 @Override 275 public Class<?> getObjectType() { 276 return CronTrigger.class; 277 } 278 279 @Override 280 public boolean isSingleton() { 281 return true; 282 } 283 284}