KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > oddjob > quartz > QuartzSchedulerJob


1 /*
2  * (c) Rob Gordon 2005
3  */

4 package org.oddjob.quartz;
5
6 import java.io.IOException JavaDoc;
7 import java.io.ObjectInputStream JavaDoc;
8 import java.io.ObjectOutputStream JavaDoc;
9 import java.io.Serializable JavaDoc;
10 import java.util.ArrayList JavaDoc;
11 import java.util.Date JavaDoc;
12 import java.util.HashMap JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.List JavaDoc;
15 import java.util.Map JavaDoc;
16 import java.util.Properties JavaDoc;
17
18 import org.apache.log4j.Logger;
19 import org.oddjob.OddjobException;
20 import org.oddjob.Stateful;
21 import org.oddjob.Structural;
22 import org.oddjob.arooa.ArooaConstants;
23 import org.oddjob.arooa.ArooaContext;
24 import org.oddjob.arooa.Lifecycle;
25 import org.oddjob.arooa.registry.ComponentRegistry;
26 import org.oddjob.arooa.registry.Path;
27 import org.oddjob.monitor.model.Describer;
28 import org.oddjob.persist.ComponentPersister;
29 import org.oddjob.scheduling.OddjobScheduler;
30 import org.oddjob.scheduling.ScheduleInstruction;
31 import org.oddjob.scheduling.ScheduleSummary;
32 import org.oddjob.state.JobState;
33 import org.oddjob.state.JobStateEvent;
34 import org.oddjob.state.JobStateHandler;
35 import org.oddjob.state.JobStateListener;
36 import org.oddjob.structural.ChildHelper;
37 import org.oddjob.structural.StructuralEvent;
38 import org.oddjob.structural.StructuralListener;
39 import org.oddjob.util.OddjobConfigException;
40 import org.quartz.Scheduler;
41 import org.quartz.SchedulerException;
42 import org.quartz.SchedulerMetaData;
43 import org.quartz.Trigger;
44 import org.quartz.impl.StdSchedulerFactory;
45
46 /**
47  * @oddjob.description Run the scheduler. The scheduler will accept a
48  * schedule instruction and produce a runtime component to handle scheduling
49  * for that insstruction. The supported schedule instructions and their
50  * corresponding runtime handler are:
51  *
52  * <table>
53  * <tr>
54  * <th>Instruction</th><th>Handler</th>
55  * </tr>
56  * <tr>
57  * <td>{@link org.oddjob.scheduling.OJScheduleInstruction}</td>
58  * <td>{@link org.oddjob.quartz.OddjobSchedule}</td>
59  * </tr>
60  * <tr>
61  * <td>{@link org.oddjob.scheduling.TriggerScheduleInstruction}</td>
62  * <td>{@link org.oddjob.quartz.TriggerSchedule}</td>
63  * </tr>
64  * <tr>
65  * <td>{@link org.oddjob.scheduling.CronScheduleInstruction}</td>
66  * <td>{@link org.oddjob.quartz.CronSchedule}</td>
67  * </tr>
68  * </table>
69  *
70  * Schedules can be specified as part of the configuration for the Scheduler
71  * or they can be added to the scheduler later using
72  * {@link org.oddjob.scheduling.ScheduleJob} or from Oddjob Explorer.
73  * <p>
74  * This job uses <a HREF="www.opensymphony.com/quartz">quartz</a> to provide
75  * the scheduling. Properties provided in the configuration are past through
76  * to quartz. For more information on these properties please see
77  * <a HREF="http://www.opensymphony.com/quartz/api/org/quartz/impl/StdSchedulerFactory.html"
78  * http://www.opensymphony.com/quartz/api/org/quartz/impl/StdSchedulerFactory.html
79  * </a>.
80  * <p>
81  * Schedules can be acessed by id using the path notation
82  * <i>${scheduler-id/schedule-id}</i>. This is useful
83  * when a property, such as a schedule date, is required from the schedule.
84  * <p>
85  * An optional Perisister can be provided to persist schedules and
86  * schedule information.
87  *
88  * @oddjob.example
89  *
90  * An alarm clock, showing also how to access the schedule time.
91  *
92  * <pre>
93  * &lt;sequential name="An Alarm Clock"&gt;
94  * &lt;scheduler id="scheduler" &gt;
95  * &lt;schedules &gt;
96  * &lt;ojschedule id="morning" job="${alarm}" &gt;
97  * &lt;schedule&gt;
98  * &lt;time on="07:00" /&gt;
99  * &lt;/schedule&gt;
100  * &lt;ojschedule
101  * &lt;/schedules&gt;
102  * &lt;/scheduler&gt;
103  * &lt;folder&gt;
104  * &lt;echo id="alarm" text="Wake Up! - it's ${scheduler/morning.scheduleDate}" /&gt;
105  * &lt;/folder&gt;
106  * &lt;/sequential&gt;
107  * </pre>
108  *
109  * Creating a schedule that persists schedule information.
110  *
111  * <pre>
112  * &lt;scheduler name="Scheduler2" id="sched2" &gt;
113  * &lt;persister&gt;
114  * &lt;filePersister dir="${this.dir}/work/scheduler" &gt;
115  * &lt;/persister&gt;
116  * &lt;/scheduler&gt;
117  * </pre>
118  *
119  * @author Rob Gordon
120  *
121  */

122 public class QuartzSchedulerJob
123 implements Structural, OddjobScheduler {
124     private static final Logger logger = Logger.getLogger(QuartzSchedulerJob.class);
125
126     /**
127      * @oddjob.property
128      * @oddjob.description A name, can be any text.
129      * @oddjob.required No.
130      */

131     private String JavaDoc name;
132     
133     /**
134      * @oddjob.property
135      * @oddjob.description Properties which will be passed through to quartz.
136      * This is a {@link org.oddjob.values.types.MapType} like property.
137      * @oddjob.required No.
138      */

139     private Properties JavaDoc properties = new Properties JavaDoc();
140         
141     /**
142      * @oddjob.property
143      * @oddjob.description A component which is able to save and restore
144      * the schedules.
145      * @oddjob.required No.
146      */

147     private ComponentPersister persister;
148         
149     /** The scheduler */
150     private Scheduler scheduler;
151     
152     /** Track the child schedule components. */
153     private ChildHelper childHelper = new ChildHelper(this);
154
155     /**
156      * @oddjob.property
157      * @oddjob.description The schedules this scheduler will run.
158      * @oddjob.required No.
159      */

160     private transient ScheduleInstruction[] schedules;
161     
162     /** Schedule jobs. */
163     private transient Map JavaDoc scheduled/*<String, QuartzSchedule>*/ = new HashMap JavaDoc();
164
165     /** Map of jobs to ids use to create summaries. */
166     private transient Map JavaDoc idsToJob /*<Srtring, Object>*/ = new HashMap JavaDoc();
167     
168     /** The registry this component is in, which child registries
169      * will be added to. */

170     private transient ComponentRegistry parentRegistry;
171     
172     /** The registry for the schedule components */
173     private transient ComponentRegistry scheduleRegistry;
174
175     /** The scheduler needs to know it's id so that it can look
176      * up the proxy instance of itself.
177      */

178     private String JavaDoc id;
179     
180     /**
181      * Set the id, called by the framework.
182      *
183      * @param id The id.
184      */

185     public void setId(String JavaDoc id) {
186         this.id = id;
187     }
188     
189     
190     /**
191      * Get the name.
192      *
193      * @return The name.
194      */

195     public String JavaDoc getName() {
196         return name;
197     }
198     
199     /**
200      * Set the name
201      *
202      * @param name The name.
203      */

204     public void setName(String JavaDoc name) {
205         this.name = name;
206     }
207
208     /**
209      * Set a property.
210      *
211      * @param name The property name.
212      * @param value The property value.
213      */

214     public void setProperty(String JavaDoc name, String JavaDoc value) {
215         properties.setProperty(name, value);
216     }
217     
218     /**
219      * Get a property.
220      *
221      * @param name The property name.
222      */

223     public String JavaDoc getProperty(String JavaDoc name) {
224         return properties.getProperty(name);
225     }
226     
227     /**
228      * Set the schedules.
229      *
230      * @param schedules The schedules.
231      */

232     public void setSchedules(ScheduleInstruction[] schedules) {
233         this.schedules = schedules;
234     }
235             
236     /**
237      * Get the schedules.
238      *
239      * @return The schedules.
240      */

241     public ScheduleInstruction[] getSchedules() {
242         return schedules;
243     }
244     
245     /**
246      * @return Returns the persistin.
247      */

248     public ComponentPersister getPersister() {
249         return persister;
250     }
251     
252     /**
253      * @param persistin The persistin to set.
254      */

255     public void setPersister(ComponentPersister persister) {
256         this.persister = persister;
257     }
258
259     /*
260      * (non-Javadoc)
261      * @see org.oddjob.scheduling.OddjobScheduler#canSchedule(java.lang.Object)
262      */

263     public boolean canSchedule(Object JavaDoc component) {
264         // will be null when can't be resolved accross the network.
265
if (component == null) {
266             return false;
267         }
268         return (scheduler != null);
269     }
270     
271     /*
272      * (non-Javadoc)
273      * @see org.oddjob.scheduling.OddjobScheduler#schedule(org.oddjob.scheduling.ScheduleInstruction)
274      */

275     public void schedule(ScheduleInstruction si) {
276         String JavaDoc id = si.getId();
277         if (id == null) {
278             throw new OddjobConfigException("Schedule has no id.");
279         }
280         synchronized (childHelper) {
281             if (scheduler == null) {
282                 throw new OddjobException("Scheduler not running!");
283             }
284             if (scheduled.get(id) != null) {
285                 logger.debug("A schedule already exists with id [" + id + "], unsheduling frist.");
286                 unSchedule(id);
287             }
288             QuartzSchedule quartzSchedule = (QuartzSchedule)
289                     new QuartzScheduleFactory().createSchedule(si,
290                             parentRegistry);
291             
292             logger.debug("Scheduling id [" + si.getId() + "].");
293             try {
294                 quartzSchedule.scheduleWith(scheduler);
295                 scheduled.put(id, quartzSchedule);
296                 idsToJob.put(id, si.getJob());
297                 scheduleRegistry.register(id, quartzSchedule);
298                 childHelper.addChild(quartzSchedule);
299             } catch (Throwable JavaDoc t) {
300                 logger.error("Failed to schedule.", t);
301             }
302         }
303         
304     }
305     
306     private void restore(String JavaDoc id) {
307         try {
308             QuartzSchedule quartzSchedule
309                 = (QuartzSchedule) persister.restore(id);
310             quartzSchedule.setComponentRegistry(parentRegistry);
311             quartzSchedule.scheduleWith(scheduler);
312             scheduled.put(id, quartzSchedule);
313 // idsToJob.put(id, si.getJob());
314
scheduleRegistry.register(id, quartzSchedule);
315             childHelper.addChild(quartzSchedule);
316         } catch (Throwable JavaDoc t) {
317             logger.error("Failed to schedule.", t);
318         }
319     }
320     
321     protected String JavaDoc[] sheduledIds() {
322         return (String JavaDoc []) idsToJob.keySet().toArray(new String JavaDoc[0]);
323     }
324     
325     /*
326      * (non-Javadoc)
327      * @see org.oddjob.scheduling.OddjobScheduler#unSchedule(java.lang.String)
328      */

329     public void unSchedule(String JavaDoc id) {
330         synchronized(childHelper) {
331             QuartzSchedule quartzSchedule = (QuartzSchedule) scheduled.get(id);
332             if (quartzSchedule == null) {
333                 return;
334             }
335             try {
336                 quartzSchedule.unscheduleFrom(scheduler);
337                 childHelper.removeChild(quartzSchedule);
338                 scheduled.remove(id);
339                 idsToJob.remove(id);
340                 scheduleRegistry.remove(id);
341                 Lifecycle.destroy(quartzSchedule);
342             } catch (Throwable JavaDoc t) {
343                 logger.error("Failed to schedule.", t);
344             }
345         }
346         
347     }
348
349     /*
350      * (non-Javadoc)
351      * @see org.oddjob.scheduling.OddjobScheduler#summariesFor(java.lang.Object)
352      */

353     public ScheduleSummary[] summariesFor(Object JavaDoc object) {
354         if (object == null) {
355             return new ScheduleSummary[0];
356         }
357         List JavaDoc results = new ArrayList JavaDoc();
358         synchronized (childHelper) {
359             for (Iterator JavaDoc it = scheduled.entrySet().iterator(); it.hasNext(); ) {
360                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
361                 String JavaDoc id = (String JavaDoc) entry.getKey();
362                 if (idsToJob.get(id) != object) {
363                     continue;
364                 }
365                 Object JavaDoc schedule = entry.getValue();
366                 Map JavaDoc description = Describer.describe(schedule);
367                 ScheduleSummary summary = new ScheduleSummary();
368                 summary.setId(id);
369                 summary.setDescription(description);
370                 results.add(summary);
371             }
372         }
373         return (ScheduleSummary[]) results.toArray(new ScheduleSummary[0]);
374     }
375         
376     /*
377      * (non-Javadoc)
378      * @see org.oddjob.framework.BaseComponent#setContext(org.oddjob.arooa.ArooaXMLContext)
379      */

380     public boolean setContext(ArooaContext context) {
381         parentRegistry = (ComponentRegistry) context.get(
382                 ArooaConstants.COMPONENT_REGISTRY);
383         return true;
384     }
385     
386     /**
387      * Start the shcheduler.
388      *
389      * @throws SchedulerException
390      */

391     public void start() throws SchedulerException {
392         StdSchedulerFactory factory = null;
393         if (properties.size() > 0) {
394             factory = new StdSchedulerFactory(properties);
395         } else {
396             factory = new StdSchedulerFactory();
397         }
398         
399         // check we have an id if we have persistence
400
if (persister != null && id == null) {
401             throw new IllegalStateException JavaDoc("Scheduler must have an Id for persistence.");
402         }
403             
404         scheduleRegistry = new ComponentRegistry();
405         Object JavaDoc maybeProxy = this;
406         // if being used in code then create a registry
407
if (parentRegistry == null) {
408             parentRegistry = new ComponentRegistry();
409         }
410         else if (id != null) {
411             maybeProxy = parentRegistry.objectForPath(new Path(id));
412         }
413         parentRegistry.addChild(scheduleRegistry, maybeProxy);
414         
415         synchronized (childHelper) {
416             scheduler = factory.getScheduler();
417             scheduler.start();
418
419             SchedulerState schedulerState = null;
420             if (persister != null) {
421                 schedulerState = (SchedulerState) persister.restore(id);
422                 // create a new StateScheduler.
423
SchedulerState newState = new SchedulerState();
424                 this.addStructuralListener(newState);
425                 scheduleRegistry.register(id, newState);
426                 persister.setRoot(newState);
427                 persister.initialise(scheduleRegistry);
428             }
429             
430             // if scheduler state is present then ignore any schedules
431
if (schedulerState != null) {
432                 for (int i = 0; schedulerState.savedIds != null
433                         && i < schedulerState.savedIds.length; ++i) {
434                     restore(schedulerState.savedIds[i]);
435                 }
436             }
437             else if (schedules != null) {
438                 for (int i = 0; i < schedules.length; ++i) {
439                         schedule(schedules[i]);
440                 }
441             }
442         }
443     }
444     
445     public void stop() throws SchedulerException {
446         // shutdown the scheduler waiting for jobs. Don't close the
447
// persister until the scheduler is closed and jobs have completed.
448
scheduler.shutdown(true);
449         scheduler = null;
450         if (persister != null) {
451             persister.close();
452             removeStructuralListener((StructuralListener) persister.getRoot());
453         }
454         parentRegistry.removeChild(this);
455         childHelper.destroyAll();
456     }
457     
458     /**
459      * Lookup a schedule in the scheduler registry.
460      *
461      * @param path The path, which will only ever be the schedule id.
462      * @return The schedule or null if none is found.
463      * @throws IllegalStateException If the scheduler is not running.
464      */

465     public Object JavaDoc lookup(String JavaDoc path) throws IllegalStateException JavaDoc {
466         if (parentRegistry == null) {
467             throw new IllegalStateException JavaDoc("Scheduler must be run before a lookup is possible");
468         }
469         ComponentRegistry componentRegistry = parentRegistry.registryOwnedBy(this);
470         if (componentRegistry == null) {
471             throw new IllegalStateException JavaDoc("Scheduler not running.");
472         }
473         Object JavaDoc found = componentRegistry.objectForPath(new Path(path));
474         logger.debug("lookup [" + path + "] found [" + found + "]");
475         return found;
476     }
477     
478     /**
479      * Add a listener. The listener will immediately recieve add
480      * notifications for all existing children.
481      *
482      * @param listener The listener.
483      */

484     public void addStructuralListener(StructuralListener listener) {
485         childHelper.addStructuralListener(listener);
486     }
487     
488     /**
489      * Remove a listener.
490      *
491      * @param listener The listner.
492      */

493     public void removeStructuralListener(StructuralListener listener) {
494         childHelper.removeStructuralListener(listener);
495     }
496     
497     /**
498      * @oddjob.property timeNow
499      * @oddjob.description The time now. This is a convenience property
500      * to allow users to see the time on the server where the scheduler
501      * is being run.
502      * @oddjob.required Read only.
503      */

504     public Date JavaDoc getTimeNow() {
505         return new Date JavaDoc();
506     }
507     
508     public String JavaDoc toString() {
509         if (name == null) {
510             return "A Scheduler";
511         }
512         return name;
513     }
514     
515     /**
516      * A backdoor way of dumping scheduler information to the log by
517      * using the set property functionality of Oddjob.
518      *
519      * @param value Can be anything.
520      * @throws SchedulerException
521      */

522     public void setLogDump(String JavaDoc value) throws SchedulerException {
523         if (scheduler == null) {
524             logger.info("Scheduler not running.");
525         }
526         SchedulerMetaData smd = scheduler.getMetaData();
527         logger.info("Scheduler Summary:" + smd.getSummary());
528
529         String JavaDoc groups[] = scheduler.getJobGroupNames();
530         for (int g = 0; g < groups.length; ++g) {
531             String JavaDoc jobs[] = scheduler.getJobNames(groups[g]);
532             for (int j = 0; j < jobs.length; ++j) {
533                 Trigger[] triggers = scheduler.getTriggersOfJob(jobs[j], groups[g]);
534                 for (int t = 0; t < triggers.length; ++t) {
535                     Trigger trigger = triggers[t];
536                     logger.info(trigger.getFullJobName()
537                             + "(" + trigger.getName() + ") : "
538                             + "Start time [" + trigger.getStartTime() + "] "
539                             + "Previous time [" + trigger.getPreviousFireTime() + "] "
540                             + "Next time [" + trigger.getNextFireTime() + "] "
541                             );
542                 }
543             }
544         }
545     }
546     
547     public static class SchedulerState
548     implements Serializable JavaDoc, Stateful, Structural, StructuralListener {
549         private static final long serialVersionUID = 20060112;
550         
551         private transient JobStateHandler stateHandler= new JobStateHandler(this);
552         
553         private String JavaDoc[] savedIds;
554         
555         private transient ChildHelper childHelper = new ChildHelper(this);
556         
557         public void childAdded(StructuralEvent event) {
558             QuartzSchedulerJob scheduler =
559                 (QuartzSchedulerJob) event.getSource();
560             savedIds = scheduler.sheduledIds();
561             childHelper.insertChild(event.getIndex(), event.getChild());
562             stateHandler.fireEvent(
563                     new JobStateEvent(this, JobState.COMPLETE));
564         }
565
566         public void childRemoved(StructuralEvent event) {
567             QuartzSchedulerJob scheduler =
568                 (QuartzSchedulerJob) event.getSource();
569             savedIds = scheduler.sheduledIds();
570             stateHandler.fireEvent(
571                     new JobStateEvent(this, JobState.COMPLETE));
572             childHelper.removeChildAt(event.getIndex());
573         }
574         
575         public void addStructuralListener(StructuralListener listener) {
576             childHelper.addStructuralListener(listener);
577         }
578         
579         public void removeStructuralListener(StructuralListener listener) {
580             childHelper.removeStructuralListener(listener);
581         }
582         
583         private void writeObject(ObjectOutputStream JavaDoc s)
584         throws IOException JavaDoc {
585             s.defaultWriteObject();
586         }
587
588         private void readObject(ObjectInputStream JavaDoc s)
589         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
590             s.defaultReadObject();
591             stateHandler = new JobStateHandler(this);
592             childHelper = new ChildHelper(this);
593         }
594         
595         public void addJobStateListener(JobStateListener listener) {
596             stateHandler.addJobStateListener(listener);
597         }
598         
599         public void removeJobStateListener(JobStateListener listener) {
600             stateHandler.removeJobStateListener(listener);
601         }
602         
603     }
604 }
605
Popular Tags