1 package org.apache.turbine.services.schedule;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.Iterator;
20 import java.util.List;
21
22 import javax.servlet.ServletConfig;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import org.apache.torque.TorqueException;
28 import org.apache.torque.util.Criteria;
29
30 import org.apache.turbine.services.InitializationException;
31 import org.apache.turbine.services.TurbineBaseService;
32 import org.apache.turbine.util.TurbineException;
33
34 /***
35 * Service for a cron like scheduler.
36 *
37 * @author <a href="mailto:mbryson@mont.mindspring.com">Dave Bryson</a>
38 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
39 * @version $Id: TurbineSchedulerService.java 264148 2005-08-29 14:21:04Z henning $
40 */
41 public class TurbineSchedulerService
42 extends TurbineBaseService
43 implements ScheduleService
44 {
45 /*** Logging */
46 private static Log log = LogFactory.getLog(ScheduleService.LOGGER_NAME);
47
48 /*** The queue */
49 protected JobQueue scheduleQueue = null;
50
51 /*** Current status of the scheduler */
52 private boolean enabled = false;
53
54 /*** The main loop for starting jobs. */
55 protected MainLoop mainLoop;
56
57 /*** The thread used to process commands. */
58 protected Thread thread;
59
60 /***
61 * Creates a new instance.
62 */
63 public TurbineSchedulerService()
64 {
65 mainLoop = null;
66 thread = null;
67 }
68
69 /***
70 * Initializes the SchedulerService.
71 *
72 * @throws InitializationException Something went wrong in the init
73 * stage
74 */
75 public void init()
76 throws InitializationException
77 {
78 try
79 {
80 setEnabled(getConfiguration().getBoolean("enabled", true));
81 scheduleQueue = new JobQueue();
82 mainLoop = new MainLoop();
83
84
85 List jobs = JobEntryPeer.doSelect(new Criteria());
86
87 if (jobs != null && jobs.size() > 0)
88 {
89 Iterator it = jobs.iterator();
90 while (it.hasNext())
91 {
92 ((JobEntry) it.next()).calcRunTime();
93 }
94 scheduleQueue.batchLoad(jobs);
95
96 restart();
97 }
98
99 setInit(true);
100 }
101 catch (Exception e)
102 {
103 String errorMessage = "Could not initialize the scheduler service";
104 log.error(errorMessage, e);
105 throw new InitializationException(errorMessage, e);
106 }
107 }
108
109 /***
110 * Called the first time the Service is used.<br>
111 *
112 * Load all the jobs from cold storage. Add jobs to the queue
113 * (sorted in ascending order by runtime) and start the scheduler
114 * thread.
115 *
116 * @param config A ServletConfig.
117 * @deprecated use init() instead.
118 */
119 public void init(ServletConfig config) throws InitializationException
120 {
121 init();
122 }
123
124 /***
125 * Shutdowns the service.
126 *
127 * This methods interrupts the housekeeping thread.
128 */
129 public void shutdown()
130 {
131 if (getThread() != null)
132 {
133 getThread().interrupt();
134 }
135 }
136
137 /***
138 * Get a specific Job from Storage.
139 *
140 * @param oid The int id for the job.
141 * @return A JobEntry.
142 * @exception TurbineException job could not be retreived.
143 */
144 public JobEntry getJob(int oid)
145 throws TurbineException
146 {
147 try
148 {
149 JobEntry je = JobEntryPeer.retrieveByPK(oid);
150 return scheduleQueue.getJob(je);
151 }
152 catch (TorqueException e)
153 {
154 String errorMessage = "Error retrieving job from persistent storage.";
155 log.error(errorMessage, e);
156 throw new TurbineException(errorMessage, e);
157 }
158 }
159
160 /***
161 * Add a new job to the queue.
162 *
163 * @param je A JobEntry with the job to add.
164 * @throws TurbineException job could not be added
165 */
166 public void addJob(JobEntry je)
167 throws TurbineException
168 {
169 updateJob(je);
170 }
171
172 /***
173 * Remove a job from the queue.
174 *
175 * @param je A JobEntry with the job to remove.
176 * @exception TurbineException job could not be removed
177 */
178 public void removeJob(JobEntry je)
179 throws TurbineException
180 {
181 try
182 {
183
184 Criteria c = new Criteria().add(JobEntryPeer.JOB_ID, je.getPrimaryKey());
185 JobEntryPeer.doDelete(c);
186
187
188 scheduleQueue.remove(je);
189
190
191 restart();
192 }
193 catch (Exception e)
194 {
195 String errorMessage = "Problem removing Scheduled Job: " + je.getTask();
196 log.error(errorMessage, e);
197 throw new TurbineException(errorMessage, e);
198 }
199 }
200
201 /***
202 * Add or update a job.
203 *
204 * @param je A JobEntry with the job to modify
205 * @throws TurbineException job could not be updated
206 */
207 public void updateJob(JobEntry je)
208 throws TurbineException
209 {
210 try
211 {
212 je.calcRunTime();
213
214
215 if (je.isNew())
216 {
217 scheduleQueue.add(je);
218 }
219 else
220 {
221 scheduleQueue.modify(je);
222 }
223
224 je.save();
225
226 restart();
227 }
228 catch (Exception e)
229 {
230 String errorMessage = "Problem updating Scheduled Job: " + je.getTask();
231 log.error(errorMessage, e);
232 throw new TurbineException(errorMessage, e);
233 }
234 }
235
236 /***
237 * List jobs in the queue. This is used by the scheduler UI.
238 *
239 * @return A List of jobs.
240 */
241 public List listJobs()
242 {
243 return scheduleQueue.list();
244 }
245
246 /***
247 * Sets the enabled status of the scheduler
248 *
249 * @param enabled
250 *
251 */
252 protected void setEnabled(boolean enabled)
253 {
254 this.enabled = enabled;
255 }
256
257 /***
258 * Determines if the scheduler service is currently enabled.
259 *
260 * @return Status of the scheduler service.
261 */
262 public boolean isEnabled()
263 {
264 return enabled;
265 }
266
267 /***
268 * Starts or restarts the scheduler if not already running.
269 */
270 public synchronized void startScheduler()
271 {
272 setEnabled(true);
273 restart();
274 }
275
276 /***
277 * Stops the scheduler if it is currently running.
278 */
279 public synchronized void stopScheduler()
280 {
281 log.info("Stopping job scheduler");
282 Thread thread = getThread();
283 if (thread != null)
284 {
285 thread.interrupt();
286 }
287 enabled = false;
288 }
289
290 /***
291 * Return the thread being used to process commands, or null if
292 * there is no such thread. You can use this to invoke any
293 * special methods on the thread, for example, to interrupt it.
294 *
295 * @return A Thread.
296 */
297 public synchronized Thread getThread()
298 {
299 return thread;
300 }
301
302 /***
303 * Set thread to null to indicate termination.
304 */
305 private synchronized void clearThread()
306 {
307 thread = null;
308 }
309
310 /***
311 * Start (or restart) a thread to process commands, or wake up an
312 * existing thread if one is already running. This method can be
313 * invoked if the background thread crashed due to an
314 * unrecoverable exception in an executed command.
315 */
316 public synchronized void restart()
317 {
318 if (enabled)
319 {
320 log.info("Starting job scheduler");
321 if (thread == null)
322 {
323
324
325
326 thread = new Thread(mainLoop, ScheduleService.SERVICE_NAME);
327
328
329
330
331 thread.setDaemon(true);
332 thread.start();
333 }
334 else
335 {
336 notify();
337 }
338 }
339 }
340
341 /***
342 * Return the next Job to execute, or null if thread is
343 * interrupted.
344 *
345 * @return A JobEntry.
346 * @exception TurbineException a generic exception.
347 */
348 private synchronized JobEntry nextJob()
349 throws TurbineException
350 {
351 try
352 {
353 while (!Thread.interrupted())
354 {
355
356 JobEntry je = scheduleQueue.getNext();
357
358 if (je == null)
359 {
360
361 wait();
362 }
363 else
364 {
365 long now = System.currentTimeMillis();
366 long when = je.getNextRuntime();
367
368 if (when > now)
369 {
370
371 wait(when - now);
372 }
373 else
374 {
375
376 scheduleQueue.updateQueue(je);
377
378 return je;
379 }
380 }
381 }
382 }
383 catch (InterruptedException ex)
384 {
385 }
386
387
388 return null;
389 }
390
391 /***
392 * Inner class. This is isolated in its own Runnable class just
393 * so that the main class need not implement Runnable, which would
394 * allow others to directly invoke run, which is not supported.
395 */
396 protected class MainLoop
397 implements Runnable
398 {
399 /***
400 * Method to run the class.
401 */
402 public void run()
403 {
404 String taskName = null;
405 try
406 {
407 while (enabled)
408 {
409 JobEntry je = nextJob();
410 if (je != null)
411 {
412 taskName = je.getTask();
413
414
415 Runnable wt = new WorkerThread(je);
416 Thread helper = new Thread(wt);
417 helper.start();
418 }
419 else
420 {
421 break;
422 }
423 }
424 }
425 catch (Exception e)
426 {
427 log.error("Error running a Scheduled Job: " + taskName, e);
428 enabled = false;
429 }
430 finally
431 {
432 clearThread();
433 }
434 }
435 }
436 }