KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > webman > util > scheduler > SchedulerMgr


1 package de.webman.util.scheduler JavaDoc;
2
3 import de.webman.util.registry.Manager;
4 import de.webman.util.registry.RegistryException;
5 import de.webman.util.log4j.WebmanCategory;
6 import org.apache.log4j.Category;
7 import java.io.File JavaDoc;
8 import java.io.IOException JavaDoc;
9 import java.io.BufferedInputStream JavaDoc;
10 import java.io.FileInputStream JavaDoc;
11 import java.util.List JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.Map JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import org.w3c.dom.Node JavaDoc;
17 import org.w3c.dom.Document JavaDoc;
18 import org.w3c.dom.Element JavaDoc;
19 import org.xml.sax.SAXException JavaDoc;
20 import org.xml.sax.InputSource JavaDoc;
21 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
22 import javax.xml.parsers.DocumentBuilder JavaDoc;
23 import javax.xml.parsers.ParserConfigurationException JavaDoc;
24 import java.text.SimpleDateFormat JavaDoc;
25 import java.text.ParseException JavaDoc;
26 import java.util.GregorianCalendar JavaDoc;
27 import java.util.Calendar JavaDoc;
28 import java.util.Date JavaDoc;
29
30
31 /**
32  * The central scheduler manager, implementing the {@link
33  * de.webman.util.Manager} protocol.<p>
34  *
35  * How to use ** TODO **<p>
36  *
37  * Format of the xml-config file:<p>
38  *
39  * <code><pre>
40  * <scheduler>
41  * <service id="gc-run"
42  * factory-class="de.webman.util.gc.GGSchedulerFactory"
43  * frequency="once"
44  * at-date="12.05.2002" at-time="20:10:45">
45  * <param key="init-file" value="gc.xml" relative="true"/>
46  * </service>
47  * <service id="sync-run"
48  * factory-class="de.webman.sync.SyncSchedulerFactory"
49  * frequency="15"
50  * freq-unit="minutes" />
51  * </scheduler>
52  * </pre></code>
53  *
54  * <code><pre>
55  * <!ELEMENT scheduler (service*)>
56  * <!ELEMENT service (param*)>
57  * <!ATTLIST service id CDATA #IMPLIED
58  * factory-class CDATA #REQUIRED
59  * frequency CDATA #REQUIRED -- may take "once" or any int number --
60  * freq-unit CDATA #IMPLIED -- "seconds|minutes|hours|days", defaults to minutes --
61  * delay CDATA #IMPLIED -- may take any int number, defaults to 0 --
62  * delay-unit CDATA #IMPLIED -- "seconds|minutes|hours|days", defaults to minutes --
63  * start-date CDATA #IMPLIED -- a date in form tt.mm.yyyy --
64  * start-time CDATA #IMPLIED -- a time in form hh:mm:ss --
65  * stop-date CDATA #IMPLIED -- a date in form tt.mm.yyyy --
66  * stop-time CDATA #IMPLIED -- a time in form hh:mm:ss --
67  * />
68  * </pre></code>
69  *
70  * @author <a HREF="mailto:gregor@webman.de">Gregor Klinke</a>
71  * @version $Revision: 1.2 $
72  **/

73 public class SchedulerMgr
74     implements Manager
75 {
76     /* $Id: SchedulerMgr.java,v 1.2 2002/04/12 12:45:53 gregor Exp $ */
77     /**
78      * logging facility
79      **/

80     private static Category cat = Category.getInstance(SchedulerMgr.class);
81
82     /**
83      * constants for time calculations (sec)
84      **/

85     public static final int MILLISECONDS_PER_SEC = 1000;
86     /**
87      * constants for time calculations (min)
88      **/

89     public static final int MILLISECONDS_PER_MIN = 1000 * 60;
90     /**
91      * constants for time calculations (hour)
92      **/

93     public static final int MILLISECONDS_PER_HOUR = 1000 * 60 * 60;
94     /**
95      * constants for time calculations (day)
96      **/

97     public static final int MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
98
99     /**
100      * date parser used for the date setting
101      **/

102     private static SimpleDateFormat JavaDoc date_format;
103
104     /**
105      * date parser used for the time setting
106      **/

107     private static SimpleDateFormat JavaDoc time_format;
108
109
110
111     /* ----------------------------------------------------------------------
112        creating and initializing
113        ---------------------------------------------------------------------- */

114     /* initialize the settings for date and time parsing */
115     static {
116         date_format = new SimpleDateFormat JavaDoc ("dd.MM.yyyy");
117         date_format.setLenient(false);
118         
119         time_format = new SimpleDateFormat JavaDoc ("HH:mm:ss");
120         time_format.setLenient(false);
121     }
122
123     /**
124      * constructor reserved for used by the factory
125      * @param basedir the base directory to use for configuration
126      * @throws RegistryException if anything failed
127      **/

128     SchedulerMgr(String JavaDoc basedir)
129         throws RegistryException
130     {
131         cat.info("setup scheduler system");
132         
133         try {
134             File JavaDoc syncfile = new File JavaDoc(basedir, "WEB-INF/classes/scheduler.xml");
135             readXMLStream(new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(syncfile)));
136             
137             // kick off!
138
startThread();
139
140             cat.info("scheduler system started successfully");
141         }
142         catch (SchedulerException se) {
143             throw new RegistryException(se);
144         }
145         catch (IOException JavaDoc ioe) {
146             cat.info ("no 'scheduler.xml' found. No schedulers started");
147         }
148     }
149     
150     
151     /* ----------------------------------------------------------------------
152        reading the configuration file and setting up the registered
153        schedulers
154        ---------------------------------------------------------------------- */

155     /* ----------------------------------------------------------------------
156        read configuration
157        ---------------------------------------------------------------------- */

158     /**
159      * reads a xml config file. for the xml structure see above
160      * @param in the stream to read the XML structure from
161      * @throws IOException guess what
162      * @throws SchedulerException something wrong with the scheduler setup
163      * @throws RegistryException something generaly wrong (mostly: can't find
164      * XML file, etc.)
165      **/

166     private void readXMLStream(BufferedInputStream JavaDoc in)
167         throws IOException JavaDoc, RegistryException, SchedulerException
168     {
169         DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
170         factory.setNamespaceAware(false);
171         factory.setValidating(false);
172             
173         Document JavaDoc doc = null;
174         try {
175             DocumentBuilder JavaDoc builder = factory.newDocumentBuilder();
176             doc = builder.parse(new InputSource JavaDoc(in));
177         }
178         catch (ParserConfigurationException JavaDoc pce) {
179             throw new RegistryException(pce);
180         }
181         catch (SAXException JavaDoc se) {
182             throw new RegistryException(se);
183         }
184         
185         Node JavaDoc c0 = doc.getDocumentElement();
186         if (!("scheduler".equals(c0.getNodeName())))
187             throw new RegistryException("bad root element '" + c0.getNodeName() + "'");
188         
189         for (Node JavaDoc c1 = c0.getFirstChild(); c1 != null; c1 = c1.getNextSibling()) {
190             if (c1.getNodeType() == Node.ELEMENT_NODE) {
191                 if ("service".equals(c1.getNodeName())) {
192                     Element JavaDoc ce = (Element JavaDoc)c1;
193                     HashMap JavaDoc prms = null;
194
195                     /* read parameters */
196                     for (Node JavaDoc c2 = c1.getFirstChild(); c2 != null; c2 = c2.getNextSibling()) {
197                         if (c2.getNodeType() == Node.ELEMENT_NODE) {
198                             if ("param".equals(c2.getNodeName())) {
199                                 Element JavaDoc c2e = (Element JavaDoc)c2;
200
201                                 String JavaDoc key = c2e.getAttribute("key");
202                                 String JavaDoc value = c2e.getAttribute("value");
203
204                                 if (prms == null)
205                                     prms = new HashMap JavaDoc();
206
207                                 prms.put(key, value);
208                             }
209                             else
210                                 throw new RegistryException("unknown element: '" + c2.getNodeName() + "'");
211                         }
212                     }
213                     
214                     setupSchedulerService(ce, prms);
215                 }
216                 else
217                     throw new RegistryException("unknown element: '" + c1.getNodeName() + "'");
218             }
219         }
220     }
221
222     /**
223      * returns the first text element below a context node
224      * @param cntx the context node
225      **/

226     private String JavaDoc getTextData(Node JavaDoc cntx) {
227         cntx.normalize();
228
229         for (Node JavaDoc n = cntx.getFirstChild(); n != null; n = n.getNextSibling()) {
230             if (n.getNodeType() == Node.TEXT_NODE) {
231                 return n.getNodeValue();
232             }
233             else if (n.getNodeType() == Node.CDATA_SECTION_NODE) {
234                 return n.getNodeValue();
235             }
236         }
237         return null;
238     }
239
240
241     /**
242      * setup a specific scheduler service
243      * @param ce the dom node to extract the parameters from
244      * @param params the preparsed parameters found below the the domnode
245      * @return <code>true</code> if the setup was successfull
246      **/

247     private boolean setupSchedulerService(Element JavaDoc ce, Map JavaDoc params)
248         throws RegistryException, SchedulerException
249     {
250         SchedulerServiceFactory sfact = null;
251         Date JavaDoc start_at = null;
252         Date JavaDoc stop_at = null;
253         long frequency = 0;
254         long delay = 0;
255         boolean start_once = false;
256         SchedulerService service_impl = null;
257
258         String JavaDoc id = ce.getAttribute("id").trim();
259         String JavaDoc freq_s = ce.getAttribute("frequency").trim();
260         String JavaDoc freq_unit_s = ce.getAttribute("freq-unit").trim();
261         String JavaDoc delay_s = ce.getAttribute("delay").trim();
262         String JavaDoc delay_unit = ce.getAttribute("delay-unit").trim();
263         String JavaDoc start_date_s = ce.getAttribute("start-date").trim();
264         String JavaDoc start_time_s = ce.getAttribute("start-time").trim();
265         String JavaDoc stop_date_s = ce.getAttribute("stop-date").trim();
266         String JavaDoc stop_time_s = ce.getAttribute("stop-time").trim();
267         String JavaDoc fact_s = ce.getAttribute("factory-class").trim();
268         
269         
270         /* [1] load the factory class */
271         try {
272             Class JavaDoc fact = Class.forName(fact_s.trim());
273             sfact = (SchedulerServiceFactory)fact.newInstance();
274         }
275         catch (Exception JavaDoc e) {
276             cat.error("Can't load scheduler service: '" + fact_s + "' (" + e + ")");
277             return false;
278         }
279
280         /* [2] parse the start information */
281         start_at = parsePointOfTime(start_date_s, start_time_s, new Date JavaDoc());
282         if (start_at == null)
283             return false;
284         
285         /* [2] parse the start information */
286         stop_at = parsePointOfTime(stop_date_s, stop_time_s, null);
287
288         /* [3] parse the frequency information */
289         start_once = "ONCE".equals(freq_s.toUpperCase());
290         if (!start_once) {
291             try {
292                 frequency = parseTimeWithUnit(freq_s, freq_unit_s);
293             }
294             catch (NumberFormatException JavaDoc nfe) {
295                 cat.error("Bad frequency setting: '" + freq_s + " " + freq_unit_s +
296                           "' for scheduler service '" + id + "' (" + nfe + ")");
297                 return false;
298             }
299
300             if (frequency <= 0) {
301                 cat.error("bad or no time setting for scheduler service '" + id + "'");
302                 return false;
303             }
304
305             try {
306                 delay = parseTimeWithUnit(delay_s, delay_unit);
307             }
308             catch (NumberFormatException JavaDoc nfe) {
309                 cat.error("Bad delay setting: '" + delay_s + " " + delay_unit +
310                           "' for scheduler service '" + id + "' (" + nfe + ")");
311                 return false;
312             }
313         }
314
315         /* [4] set the params to the factory */
316         if (params != null) {
317             for (Iterator JavaDoc it = params.keySet().iterator(); it.hasNext(); ) {
318                 String JavaDoc key = (String JavaDoc)it.next();
319                 sfact.setProperty(key, params.get(key));
320             }
321         }
322         
323         /* [5] register the service */
324         if (start_once)
325             registerOneTimeService(id, start_at, sfact);
326         else
327             registerFrequentService(id, start_at, stop_at, frequency, delay, sfact);
328         
329         return true;
330     }
331
332
333     /**
334      * parses a date (format: dd.mm.yyyy) and time (format: hh:mm:ss) string
335      * @param at_date the date string properly trimmed
336      * @param at_time the time string properly trimmed
337      * @return the resulting date or <code>null</code> if a parse exception
338      * (or similar) occured
339      * @param def the default date to use if no valid date could be parsed
340      * @return the date recognized
341      **/

342     private Date JavaDoc parsePointOfTime(String JavaDoc at_date, String JavaDoc at_time, Date JavaDoc def)
343     {
344         Date JavaDoc start_at_date = null;
345         Date JavaDoc start_at_time = null;
346         
347         /* [2] parse the date and time information */
348         if (at_date != null && at_date.length() > 0) {
349             try {
350                 start_at_date = date_format.parse(at_date);
351             }
352             catch (ParseException JavaDoc pe) {
353                 cat.error("bad date setting: '" + pe + "'");
354                 return null;
355             }
356         }
357
358         if (at_time != null && at_time.length() > 0) {
359             try {
360                 start_at_time = time_format.parse(at_time);
361             }
362             catch (ParseException JavaDoc pe) {
363                 cat.error("bad time setting: '" + pe + "'");
364                 return null;
365             }
366         }
367
368         if (start_at_date != null && start_at_time != null) {
369             Calendar JavaDoc dcal = Calendar.getInstance();
370             Calendar JavaDoc tcal = Calendar.getInstance();
371             dcal.setTime(start_at_date);
372             tcal.setTime(start_at_time);
373             
374             return new GregorianCalendar JavaDoc(dcal.get(Calendar.YEAR),
375                                          dcal.get(Calendar.MONTH),
376                                          dcal.get(Calendar.DAY_OF_MONTH),
377                                          tcal.get(Calendar.HOUR_OF_DAY),
378                                          tcal.get(Calendar.MINUTE),
379                                          tcal.get(Calendar.SECOND)).getTime();
380         }
381         else if (start_at_date != null) {
382             return start_at_date;
383         }
384         else if (start_at_time != null) {
385             Calendar JavaDoc dcal = Calendar.getInstance(); // relative to now!
386
Calendar JavaDoc tcal = Calendar.getInstance();
387             tcal.setTime(start_at_time);
388             return new GregorianCalendar JavaDoc(dcal.get(Calendar.YEAR),
389                                          dcal.get(Calendar.MONTH),
390                                          dcal.get(Calendar.DAY_OF_MONTH),
391                                          tcal.get(Calendar.HOUR_OF_DAY),
392                                          tcal.get(Calendar.MINUTE),
393                                          tcal.get(Calendar.SECOND)).getTime();
394         }
395         
396         return def;
397     }
398
399
400     /**
401      * parses a gap-of-time information according to a unit. The units
402      * known are: minutes, seconds, days, hours
403      * @param tmstr the gap-of-time string
404      * @param unit the unit string, if <code>null</code> or empty defaults to "minute"
405      * @return the time computed to milliseconds
406      *
407      * @throws NumberFormatException if the timestring was bad or an
408      * unknown unit was used.
409      **/

410     private long parseTimeWithUnit(String JavaDoc tmstr, String JavaDoc unit)
411         throws NumberFormatException JavaDoc
412     {
413         long tm = Long.parseLong(tmstr);
414
415         if (unit != null && unit.length() > 0) {
416             if ("SECONDS".equals(unit.toUpperCase()))
417                 tm *= MILLISECONDS_PER_SEC;
418             else if ("MINUTES".equals(unit.toUpperCase()))
419                 tm *= MILLISECONDS_PER_MIN;
420             else if ("HOURS".equals(unit.toUpperCase()))
421                 tm *= MILLISECONDS_PER_HOUR;
422             else if ("DAYS".equals(unit.toUpperCase()))
423                 tm *= MILLISECONDS_PER_DAY;
424             else
425                 throw new NumberFormatException JavaDoc("unknown time unit: '" + unit + "'");
426         }
427         else
428             tm *= MILLISECONDS_PER_MIN;
429         
430         return tm;
431     }
432
433
434
435     /* ----------------------------------------------------------------------
436        the scheduler master thread stuff
437        ---------------------------------------------------------------------- */

438     /**
439      * default sleep time
440      **/

441     private final static long DEFAULT_CHECK_TIME = 10 * 1000; // 5 second
442

443     /**
444      * minimum check time
445      **/

446     private final static long MIN_CHECK_TIME = 1000; // 1 second
447

448     /**
449      * the list of schedulers
450      **/

451     private ArrayList JavaDoc schedulers = new ArrayList JavaDoc();
452
453     /**
454      * the iterator for the scheduler checking
455      **/

456     private Iterator JavaDoc checker = null;
457
458     /**
459      * the scheduler thread
460      **/

461     private SchedulerThread thread = null;
462     
463     /**
464      * the time between two checks for due schedulers
465      **/

466     private long sleepTime = DEFAULT_CHECK_TIME;
467
468     /**
469      * kicks off the scheduling check thread
470      **/

471     private void startThread() {
472         if (thread == null && schedulers.size() > 0) {
473             thread = new SchedulerThread(this);
474             Thread JavaDoc t = new Thread JavaDoc(thread);
475             
476             /* the timer thread is a time daemon only. */
477             t.setDaemon(true);
478             t.start();
479         }
480     }
481     
482     /**
483      * checks the next scheduler in the scheduler list
484      **/

485     void checkNext() {
486         cat.info("check next");
487         synchronized(schedulers) {
488             if (checker == null || !checker.hasNext()) {
489                 checker = schedulers.iterator();
490                 if (!checker.hasNext()) {
491                     thread.stop();
492                     thread = null;
493                     sleepTime = DEFAULT_CHECK_TIME;
494                 }
495             }
496
497             ServiceEntry srve = (ServiceEntry)checker.next();
498             Date JavaDoc now = new Date JavaDoc();
499             if (srve.isDue(now))
500                 srve.executeNewService();
501             
502             if (srve.isOutdated(now)) {
503                 cat.debug ("remove outdated service + '" + srve.id + "'");
504                 checker.remove();
505             }
506         }
507     }
508
509     /**
510      * returns the current sleep time for the timer thread
511      **/

512     long getSleepTime() {
513         return sleepTime;
514     }
515
516
517     /**
518      * adds a service entry to the list of services.
519      * @param se the service entry to add, must not be <code>null</code>
520      * @param _hint a time hint to use. this is normaly the frequency, in
521      * which the service should be used.
522      **/

523     private void addService(ServiceEntry se, long _hint) {
524         synchronized(schedulers) {
525             cat.info("register scheduler service '" + se.id + "' (" +
526                      se.factory.getClass().getName() + ") hint at: " + _hint);
527             schedulers.add(se);
528
529             /* if the hinted time frame is smalled than the actually sleep
530                time frame, use this one */

531             if (_hint < sleepTime) {
532                 if (_hint > 0)
533                     sleepTime = _hint + MIN_CHECK_TIME;
534                 else
535                     sleepTime = MIN_CHECK_TIME;
536             }
537             if (thread == null)
538                 startThread();
539             
540             /* adding an object, makes the iterator unusable */
541             checker = null;
542         }
543     }
544     
545
546     /**
547      * registers a scheduler service, to be called on a regular base. This
548      * frequent repetition is characteristized by a start and stop point in
549      * time, a start off delay and a repetition frequency.
550      * @param _id a descriptive id of the service
551      * @param _start_at start point
552      * @param _stop_at stop point, if <code>null</code> stops never
553      * @param _frequency the repetition frequency in milliseconds
554      * @param _delay the start off delay in milliseconds, relative to _start_at
555      * @param _sfact the scheduler services factory class
556      **/

557     public void registerFrequentService(String JavaDoc _id,
558                                         Date JavaDoc _start_at, Date JavaDoc _stop_at,
559                                         long _frequency, long _delay,
560                                         SchedulerServiceFactory _sfact)
561     {
562         addService(new FrequentService(_id, _start_at, _stop_at, _frequency, _delay, _sfact),
563                    _frequency);
564     }
565     
566     /**
567      * registers a scheduler service, to be called excatly once, sometime
568      * in the future.
569      * @param _id a descriptive id of the service
570      * @param _start_at the point in time to start the service
571      * @param _sfact the scheduler services factory class
572      **/

573     public void registerOneTimeService(String JavaDoc _id,
574                                        Date JavaDoc _start_at,
575                                        SchedulerServiceFactory _sfact)
576     {
577         Calendar JavaDoc cal = Calendar.getInstance();
578         cal.setTime(_start_at);
579         long check_time = System.currentTimeMillis() - cal.getTime().getTime();
580         
581         addService(new OneTimeService(_id, _start_at, _sfact), check_time);
582     }
583 }
584
585
Popular Tags