%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.turbine.services.schedule.TurbineSchedulerService |
|
|
1 | package org.apache.turbine.services.schedule; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2005 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
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 | 6 | public class TurbineSchedulerService |
42 | extends TurbineBaseService |
|
43 | implements ScheduleService |
|
44 | { |
|
45 | /** Logging */ |
|
46 | 2 | private static Log log = LogFactory.getLog(ScheduleService.LOGGER_NAME); |
47 | ||
48 | /** The queue */ |
|
49 | 2 | protected JobQueue scheduleQueue = null; |
50 | ||
51 | /** Current status of the scheduler */ |
|
52 | 2 | 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 | 2 | { |
65 | 2 | mainLoop = null; |
66 | 2 | thread = null; |
67 | 2 | } |
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 | 0 | setEnabled(getConfiguration().getBoolean("enabled", true)); |
81 | 0 | scheduleQueue = new JobQueue(); |
82 | 0 | mainLoop = new MainLoop(); |
83 | ||
84 | // Load all from cold storage. |
|
85 | 0 | List jobs = JobEntryPeer.doSelect(new Criteria()); |
86 | ||
87 | 0 | if (jobs != null && jobs.size() > 0) |
88 | { |
|
89 | 0 | Iterator it = jobs.iterator(); |
90 | 0 | while (it.hasNext()) |
91 | { |
|
92 | 0 | ((JobEntry) it.next()).calcRunTime(); |
93 | } |
|
94 | 0 | scheduleQueue.batchLoad(jobs); |
95 | ||
96 | 0 | restart(); |
97 | } |
|
98 | ||
99 | 0 | setInit(true); |
100 | } |
|
101 | 0 | catch (Exception e) |
102 | { |
|
103 | 0 | String errorMessage = "Could not initialize the scheduler service"; |
104 | 0 | log.error(errorMessage, e); |
105 | 0 | throw new InitializationException(errorMessage, e); |
106 | 0 | } |
107 | 0 | } |
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 | 0 | init(); |
122 | 0 | } |
123 | ||
124 | /** |
|
125 | * Shutdowns the service. |
|
126 | * |
|
127 | * This methods interrupts the housekeeping thread. |
|
128 | */ |
|
129 | public void shutdown() |
|
130 | { |
|
131 | 0 | if (getThread() != null) |
132 | { |
|
133 | 0 | getThread().interrupt(); |
134 | } |
|
135 | 0 | } |
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 | 0 | JobEntry je = JobEntryPeer.retrieveByPK(oid); |
150 | 0 | return scheduleQueue.getJob(je); |
151 | } |
|
152 | 0 | catch (TorqueException e) |
153 | { |
|
154 | 0 | String errorMessage = "Error retrieving job from persistent storage."; |
155 | 0 | log.error(errorMessage, e); |
156 | 0 | 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 | 0 | updateJob(je); |
170 | 0 | } |
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 | // First remove from DB. |
|
184 | 0 | Criteria c = new Criteria().add(JobEntryPeer.JOB_ID, je.getPrimaryKey()); |
185 | 0 | JobEntryPeer.doDelete(c); |
186 | ||
187 | // Remove from the queue. |
|
188 | 0 | scheduleQueue.remove(je); |
189 | ||
190 | // restart the scheduler |
|
191 | 0 | restart(); |
192 | } |
|
193 | 0 | catch (Exception e) |
194 | { |
|
195 | 0 | String errorMessage = "Problem removing Scheduled Job: " + je.getTask(); |
196 | 0 | log.error(errorMessage, e); |
197 | 0 | throw new TurbineException(errorMessage, e); |
198 | 0 | } |
199 | 0 | } |
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 | 0 | je.calcRunTime(); |
213 | ||
214 | // Update the queue. |
|
215 | 0 | if (je.isNew()) |
216 | { |
|
217 | 0 | scheduleQueue.add(je); |
218 | } |
|
219 | else |
|
220 | { |
|
221 | 0 | scheduleQueue.modify(je); |
222 | } |
|
223 | ||
224 | 0 | je.save(); |
225 | ||
226 | 0 | restart(); |
227 | } |
|
228 | 0 | catch (Exception e) |
229 | { |
|
230 | 0 | String errorMessage = "Problem updating Scheduled Job: " + je.getTask(); |
231 | 0 | log.error(errorMessage, e); |
232 | 0 | throw new TurbineException(errorMessage, e); |
233 | 0 | } |
234 | 0 | } |
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 | 6 | 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 | 4 | this.enabled = enabled; |
255 | 4 | } |
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 | 4 | return enabled; |
265 | } |
|
266 | ||
267 | /** |
|
268 | * Starts or restarts the scheduler if not already running. |
|
269 | */ |
|
270 | public synchronized void startScheduler() |
|
271 | { |
|
272 | 2 | setEnabled(true); |
273 | 2 | restart(); |
274 | 2 | } |
275 | ||
276 | /** |
|
277 | * Stops the scheduler if it is currently running. |
|
278 | */ |
|
279 | public synchronized void stopScheduler() |
|
280 | { |
|
281 | 2 | log.info("Stopping job scheduler"); |
282 | 2 | Thread thread = getThread(); |
283 | 2 | if (thread != null) |
284 | { |
|
285 | 2 | thread.interrupt(); |
286 | } |
|
287 | 2 | enabled = false; |
288 | 2 | } |
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 | 2 | return thread; |
300 | } |
|
301 | ||
302 | /** |
|
303 | * Set thread to null to indicate termination. |
|
304 | */ |
|
305 | private synchronized void clearThread() |
|
306 | { |
|
307 | 2 | thread = null; |
308 | 2 | } |
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 | 8 | if (enabled) |
319 | { |
|
320 | 4 | log.info("Starting job scheduler"); |
321 | 4 | if (thread == null) |
322 | { |
|
323 | // Create the the housekeeping thread of the scheduler. It will wait |
|
324 | // for the time when the next task needs to be started, and then |
|
325 | // launch a worker thread to execute the task. |
|
326 | 2 | thread = new Thread(mainLoop, ScheduleService.SERVICE_NAME); |
327 | // Indicate that this is a system thread. JVM will quit only when there |
|
328 | // are no more enabled user threads. Settings threads spawned internally |
|
329 | // by Turbine as daemons allows commandline applications using Turbine |
|
330 | // to terminate in an orderly manner. |
|
331 | 2 | thread.setDaemon(true); |
332 | 2 | thread.start(); |
333 | } |
|
334 | else |
|
335 | { |
|
336 | 2 | notify(); |
337 | } |
|
338 | } |
|
339 | 8 | } |
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 | 5 | while (!Thread.interrupted()) |
354 | { |
|
355 | // Grab the next job off the queue. |
|
356 | 4 | JobEntry je = scheduleQueue.getNext(); |
357 | ||
358 | 4 | if (je == null) |
359 | { |
|
360 | // Queue must be empty. Wait on it. |
|
361 | 0 | wait(); |
362 | } |
|
363 | else |
|
364 | { |
|
365 | 4 | long now = System.currentTimeMillis(); |
366 | 4 | long when = je.getNextRuntime(); |
367 | ||
368 | 4 | if (when > now) |
369 | { |
|
370 | // Wait till next runtime. |
|
371 | 4 | wait(when - now); |
372 | } |
|
373 | else |
|
374 | { |
|
375 | // Update the next runtime for the job. |
|
376 | 0 | scheduleQueue.updateQueue(je); |
377 | // Return the job to run it. |
|
378 | 0 | return je; |
379 | } |
|
380 | } |
|
381 | } |
|
382 | } |
|
383 | 1 | catch (InterruptedException ex) |
384 | 1 | { |
385 | 0 | } |
386 | ||
387 | // On interrupt. |
|
388 | 2 | 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 | // Start the thread to run the job. |
|
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 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |