001/*
002 * Copyright 2002-2016 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.util.Assert;
035
036/**
037 * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.CronTrigger}
038 * instance, supporting bean-style usage for trigger configuration.
039 *
040 * <p>{@code CronTrigger(Impl)} itself is already a JavaBean but lacks sensible defaults.
041 * This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT")
042 * as job group, the current time as start time, and indefinite repetition, if not specified.
043 *
044 * <p>This class will also register the trigger with the job name and group of
045 * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
046 * to automatically register a trigger for the corresponding JobDetail,
047 * instead of registering the JobDetail separately.
048 *
049 * @author Juergen Hoeller
050 * @since 3.1
051 * @see #setName
052 * @see #setGroup
053 * @see #setStartDelay
054 * @see #setJobDetail
055 * @see SchedulerFactoryBean#setTriggers
056 * @see SchedulerFactoryBean#setJobDetails
057 */
058public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNameAware, InitializingBean {
059
060        /** Constants for the CronTrigger class */
061        private static final Constants constants = new Constants(CronTrigger.class);
062
063
064        private String name;
065
066        private String group;
067
068        private JobDetail jobDetail;
069
070        private JobDataMap jobDataMap = new JobDataMap();
071
072        private Date startTime;
073
074        private long startDelay = 0;
075
076        private String cronExpression;
077
078        private TimeZone timeZone;
079
080        private String calendarName;
081
082        private int priority;
083
084        private int misfireInstruction;
085
086        private String description;
087
088        private String beanName;
089
090        private CronTrigger cronTrigger;
091
092
093        /**
094         * Specify the trigger's name.
095         */
096        public void setName(String name) {
097                this.name = name;
098        }
099
100        /**
101         * Specify the trigger's group.
102         */
103        public void setGroup(String group) {
104                this.group = group;
105        }
106
107        /**
108         * Set the JobDetail that this trigger should be associated with.
109         */
110        public void setJobDetail(JobDetail jobDetail) {
111                this.jobDetail = jobDetail;
112        }
113
114        /**
115         * Set the trigger's JobDataMap.
116         * @see #setJobDataAsMap
117         */
118        public void setJobDataMap(JobDataMap jobDataMap) {
119                this.jobDataMap = jobDataMap;
120        }
121
122        /**
123         * Return the trigger's JobDataMap.
124         */
125        public JobDataMap getJobDataMap() {
126                return this.jobDataMap;
127        }
128
129        /**
130         * Register objects in the JobDataMap via a given Map.
131         * <p>These objects will be available to this Trigger only,
132         * in contrast to objects in the JobDetail's data map.
133         * @param jobDataAsMap Map with String keys and any objects as values
134         * (for example Spring-managed beans)
135         */
136        public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
137                this.jobDataMap.putAll(jobDataAsMap);
138        }
139
140        /**
141         * Set a specific start time for the trigger.
142         * <p>Note that a dynamically computed {@link #setStartDelay} specification
143         * overrides a static timestamp set here.
144         */
145        public void setStartTime(Date startTime) {
146                this.startTime = startTime;
147        }
148
149        /**
150         * Set the start delay in milliseconds.
151         * <p>The start delay is added to the current system time (when the bean starts)
152         * to control the start time of the trigger.
153         */
154        public void setStartDelay(long startDelay) {
155                Assert.isTrue(startDelay >= 0, "Start delay cannot be negative");
156                this.startDelay = startDelay;
157        }
158
159        /**
160         * Specify the cron expression for this trigger.
161         */
162        public void setCronExpression(String cronExpression) {
163                this.cronExpression = cronExpression;
164        }
165
166        /**
167         * Specify the time zone for this trigger's cron expression.
168         */
169        public void setTimeZone(TimeZone timeZone) {
170                this.timeZone = timeZone;
171        }
172
173        /**
174         * Associate a specific calendar with this cron trigger.
175         */
176        public void setCalendarName(String calendarName) {
177                this.calendarName = calendarName;
178        }
179
180        /**
181         * Specify the priority of this trigger.
182         */
183        public void setPriority(int priority) {
184                this.priority = priority;
185        }
186
187        /**
188         * Specify a misfire instruction for this trigger.
189         */
190        public void setMisfireInstruction(int misfireInstruction) {
191                this.misfireInstruction = misfireInstruction;
192        }
193
194        /**
195         * Set the misfire instruction via the name of the corresponding
196         * constant in the {@link org.quartz.CronTrigger} class.
197         * Default is {@code MISFIRE_INSTRUCTION_SMART_POLICY}.
198         * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
199         * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
200         * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
201         */
202        public void setMisfireInstructionName(String constantName) {
203                this.misfireInstruction = constants.asNumber(constantName).intValue();
204        }
205
206        /**
207         * Associate a textual description with this trigger.
208         */
209        public void setDescription(String description) {
210                this.description = description;
211        }
212
213        @Override
214        public void setBeanName(String beanName) {
215                this.beanName = beanName;
216        }
217
218
219        @Override
220        public void afterPropertiesSet() throws ParseException {
221                if (this.name == null) {
222                        this.name = this.beanName;
223                }
224                if (this.group == null) {
225                        this.group = Scheduler.DEFAULT_GROUP;
226                }
227                if (this.jobDetail != null) {
228                        this.jobDataMap.put("jobDetail", this.jobDetail);
229                }
230                if (this.startDelay > 0 || this.startTime == null) {
231                        this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
232                }
233                if (this.timeZone == null) {
234                        this.timeZone = TimeZone.getDefault();
235                }
236
237                CronTriggerImpl cti = new CronTriggerImpl();
238                cti.setName(this.name);
239                cti.setGroup(this.group);
240                if (this.jobDetail != null) {
241                        cti.setJobKey(this.jobDetail.getKey());
242                }
243                cti.setJobDataMap(this.jobDataMap);
244                cti.setStartTime(this.startTime);
245                cti.setCronExpression(this.cronExpression);
246                cti.setTimeZone(this.timeZone);
247                cti.setCalendarName(this.calendarName);
248                cti.setPriority(this.priority);
249                cti.setMisfireInstruction(this.misfireInstruction);
250                cti.setDescription(this.description);
251                this.cronTrigger = cti;
252        }
253
254
255        @Override
256        public CronTrigger getObject() {
257                return this.cronTrigger;
258        }
259
260        @Override
261        public Class<?> getObjectType() {
262                return CronTrigger.class;
263        }
264
265        @Override
266        public boolean isSingleton() {
267                return true;
268        }
269
270}