1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.scheduler;
35
36 import info.magnolia.module.ModuleLifecycle;
37 import info.magnolia.module.ModuleLifecycleContext;
38
39 import java.text.ParseException;
40 import java.util.ArrayList;
41 import java.util.Iterator;
42 import java.util.List;
43
44 import org.apache.commons.lang.StringUtils;
45 import org.quartz.CronTrigger;
46 import org.quartz.JobDetail;
47 import org.quartz.Scheduler;
48 import org.quartz.SchedulerException;
49 import org.quartz.SchedulerFactory;
50 import org.quartz.Trigger;
51 import org.quartz.impl.StdSchedulerFactory;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60 public class SchedulerModule implements ModuleLifecycle {
61 private static final Logger log = LoggerFactory.getLogger(JobDefinition.class);
62
63 private static SchedulerModule instance;
64
65 private List<JobDefinition> jobs = new ArrayList<JobDefinition>();
66
67 private boolean running = false;
68
69
70
71
72 protected Scheduler scheduler;
73
74 public SchedulerModule() {
75 instance = this;
76 }
77
78 public List<JobDefinition> getJobs() {
79 return this.jobs;
80 }
81
82 public void setJobs(List<JobDefinition> jobs) throws SchedulerException {
83 this.jobs = jobs;
84 for (JobDefinition job : jobs) {
85 if (running) {
86 initJob(job);
87 }
88 }
89 }
90
91 public void addJob(JobDefinition job) throws SchedulerException {
92 jobs.add(job);
93 if (running) {
94 initJob(job);
95 }
96 }
97
98
99
100
101 @Override
102 public void stop(ModuleLifecycleContext moduleLifecycleContext) {
103 try {
104 scheduler.shutdown(true);
105 log.info("Waiting up to 30 seconds for scheduled jobs to stop...");
106 int count = 0;
107 while (count < 30) {
108 try {
109 Thread.sleep(1000);
110 } catch (InterruptedException e) {
111 Thread.interrupted();
112 }
113 if (scheduler.isShutdown()) {
114 break;
115 } else {
116 count++;
117 }
118 }
119 if (!scheduler.isShutdown()) {
120 log.info("Scheduled jobs failed to finish in 30 seconds interval, forcing shutdown now...");
121 scheduler.shutdown(false);
122 }
123 running = false;
124 } catch (SchedulerException e) {
125 log.error("Can't stop scheduler properly", e);
126 }
127 }
128
129
130
131
132 @Override
133 public void start(ModuleLifecycleContext moduleLifecycleContext) {
134 try {
135 initScheduler();
136 running = true;
137 } catch (SchedulerException e) {
138 log.error("Can't start scheduler", e);
139 return;
140 }
141
142 initJobs();
143 }
144
145
146
147
148 protected void initJobs() {
149 for (Iterator iter = jobs.iterator(); iter.hasNext();) {
150 JobDefinition job = (JobDefinition) iter.next();
151 try {
152 initJob(job);
153 } catch (SchedulerException e) {
154 log.error("Can't initialize job [" + job.getName() + "]", e);
155 }
156 }
157 }
158
159
160
161
162 protected void initJob(JobDefinition job) throws SchedulerException {
163 if (job.isActive()) {
164 try {
165 stopJob(job.getName());
166 startJob(job);
167 } catch (SchedulerException e) {
168 throw new SchedulerException("Can't schedule job" + job.getName(), e);
169 }
170 }
171 else {
172 try {
173 stopJob(job.getName());
174 } catch (SchedulerException e) {
175 throw new SchedulerException("Can't delete inactive job " + job.getName(), e);
176 }
177 }
178 }
179
180 protected void startJob(JobDefinition job) throws SchedulerException {
181 Trigger trigger;
182 try {
183 String cron = cronToQuarzCron(job.getCron());
184 trigger = new CronTrigger(job.getName(), SchedulerConsts.SCHEDULER_GROUP_NAME, cron);
185 } catch (ParseException e) {
186 log.error("Can't parse the job's cron expression [" + job.getCron() + "]", e);
187 return;
188 }
189
190 final Class jobClass = job.isConcurrent() ? CommandJob.class : StatefulCommandJob.class;
191 final JobDetail jd = new JobDetail(job.getName(), SchedulerConsts.SCHEDULER_GROUP_NAME, jobClass);
192 jd.getJobDataMap().put(SchedulerConsts.CONFIG_JOB_COMMAND, job.getCommand());
193 jd.getJobDataMap().put(SchedulerConsts.CONFIG_JOB_COMMAND_CATALOG, job.getCatalog());
194 jd.getJobDataMap().put(SchedulerConsts.CONFIG_JOB_PARAMS, job.getParams());
195 scheduler.scheduleJob(jd, trigger);
196 log.info("Job " + job.getName() + " added [" + job.getCron() + "]. Will fire first time at " + trigger.getNextFireTime());
197 }
198
199 protected String cronToQuarzCron(String cron) {
200
201 String[] tokens = StringUtils.split(cron);
202 if (tokens.length >= 6) {
203 if (!tokens[3].equals("?") && !tokens[5].equals("?")) {
204 if (tokens[5].equals("*")) {
205 tokens[5] = "?";
206 }
207 else if (tokens[3].equals("*")) {
208 tokens[3] = "?";
209 }
210 }
211 }
212 cron = StringUtils.join(tokens, " ");
213 return cron;
214 }
215
216
217
218
219 public void stopJob(String name) throws SchedulerException {
220 scheduler.deleteJob(name, SchedulerConsts.SCHEDULER_GROUP_NAME);
221 }
222
223
224
225
226
227
228 protected void initScheduler() throws SchedulerException {
229 SchedulerFactory sf = new StdSchedulerFactory();
230 scheduler = sf.getScheduler();
231 scheduler.start();
232 }
233
234
235
236
237 public Scheduler getScheduler() {
238 return scheduler;
239 }
240
241
242
243
244
245
246 public static SchedulerModule getInstance() {
247 return instance;
248 }
249
250 }