KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > services > crontab > CrontabEntry


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64 package com.jcorporate.expresso.services.crontab;
65
66 import org.apache.log4j.Logger;
67
68 import java.util.Calendar JavaDoc;
69 import java.util.Date JavaDoc;
70
71
72 /**
73  * CrontabEntry represents a repeatable 'action' that can take place at a given
74  * date and time.
75  * <p/>
76  * <p/>
77  * A crontab entry's sort order creates differences between Comparable and
78  * equals(). the objects are compared by the execution time. They are tested
79  * for equality by testing time AND the listeners
80  * </p>
81  *
82  * @author Mike Dubman
83  */

84 public class CrontabEntry implements Comparable JavaDoc, java.io.Serializable JavaDoc {
85     private static final Logger log = Logger.getLogger(CrontabEntry.class);
86
87     /**
88      * Constant that represents a 'time' value that is unused.
89      */

90     public static final int TIME_UNUSED = -1;
91
92     /**
93      *
94      */

95     protected static long globalCounter = 0;
96
97     /**
98      *
99      */

100     protected static Object JavaDoc counterLock = new Object JavaDoc();
101
102     /**
103      * The listener of the crontab
104      */

105     protected transient CrontabListenerI listener;
106
107     /**
108      * Label for the crontab
109      */

110     protected String JavaDoc label = "";
111
112     /**
113      * Is the crontab a 'relative' time?
114      */

115     protected boolean isRelative;
116
117     /**
118      * Is the crontab a repetitive job
119      */

120     protected boolean isRepetitive;
121
122     /**
123      * day of month for the crontab
124      */

125     protected int dayOfMonth = TIME_UNUSED;
126
127     /**
128      * day of week for the crontab
129      */

130     protected int dayOfWeek = TIME_UNUSED;
131
132     /**
133      * Hours for the crontab
134      */

135     protected int hour = TIME_UNUSED;
136
137     /**
138      * The minutes for the crontab
139      */

140     protected int minute = TIME_UNUSED;
141
142     /**
143      * month for the crontab
144      */

145     protected int month = TIME_UNUSED;
146
147     /**
148      * year for the crontab
149      */

150     protected int year = TIME_UNUSED;
151
152     /**
153      * When is the next time for the alarm
154      */

155     protected long alarmTime;
156
157     /**
158      * Unique id for sorting.
159      */

160     protected long counterValue;
161
162     /**
163      * Job Number for this CrontabEntry's associated JobQueue entry
164      */

165     protected String JavaDoc jobNumber = null;
166
167     /**
168      * Constructs a Crontab Entry class.
169      *
170      * @param date The date to execute the crontab listener
171      * @param listener The class to execute when the alarm 'rings'
172      * @throws CronException upon error
173      */

174     public CrontabEntry(Date JavaDoc date, CrontabListenerI listener)
175             throws CronException {
176         this.listener = listener;
177
178         setCounterValue();
179
180         Calendar JavaDoc alarm = Calendar.getInstance();
181         alarm.setTime(date);
182         minute = alarm.get(Calendar.MINUTE);
183         hour = alarm.get(Calendar.HOUR_OF_DAY);
184         dayOfMonth = alarm.get(Calendar.DAY_OF_MONTH);
185         month = alarm.get(Calendar.MONTH);
186         year = alarm.get(Calendar.YEAR);
187         isRepetitive = false;
188         isRelative = false;
189         alarmTime = date.getTime();
190         checkEntryTime();
191
192         if (log.isDebugEnabled()) {
193             log.debug("Constructed new crontab entry: " + this.toString());
194         }
195     }
196
197     /**
198      * Construct a new crontab entry with a given delay
199      *
200      * @param delay the job until the number of minutes in the next hour
201      * @param isRepetitive set if the job should be executed every X amount of
202      * time
203      * @param listener the interface to notify when the cron 'rings'
204      * @throws IllegalArgumentException if the delay is less than 1
205      */

206     public CrontabEntry(int delay, boolean isRepetitive, CrontabListenerI listener) {
207         if (delay < 1) {
208             throw new IllegalArgumentException JavaDoc("Parameter delay must be >= 1");
209         }
210
211         setCounterValue();
212
213         minute = delay;
214         this.listener = listener;
215         this.isRepetitive = isRepetitive;
216
217         isRelative = true;
218         updateEntryTime();
219     }
220
221     /**
222      * Construct a Crontab entry.
223      *
224      * @param minute Minute of the hour to execute
225      * @param hour Hour of the day to execute
226      * @param dayOfMonth The day of the month to execute
227      * @param month The month of the year to execute
228      * @param dayOfWeek The day of the week to execute
229      * @param year to execute OR TIME_UNUSED if this is a repetative crontab
230      * entry
231      * @param listener The class that gets called when the crontab 'rings'
232      * @throws CronException upon error
233      */

234     public CrontabEntry(int minute, int hour, int dayOfMonth, int month,
235                         int dayOfWeek, int year, CrontabListenerI listener)
236             throws CronException {
237         setCounterValue();
238
239         this.minute = minute;
240         this.hour = hour;
241         this.dayOfMonth = dayOfMonth;
242         this.month = month;
243         this.dayOfWeek = dayOfWeek;
244         this.year = year;
245         this.listener = listener;
246         this.label = "Unlabelled Crontab Entry";
247         isRepetitive = (year == TIME_UNUSED);
248         isRelative = false;
249         updateEntryTime();
250         checkEntryTime();
251
252         if (log.isDebugEnabled()) {
253             log.debug("Constructed new crontab entry: " + this.toString());
254         }
255     }
256
257     /**
258      * Construct a Crontab entry.
259      *
260      * @param minute Minute of the hour to execute
261      * @param hour Hour of the day to execute
262      * @param dayOfMonth The day of the month to execute
263      * @param month The month of the year to execute
264      * @param dayOfWeek The day of the week to execute
265      * @param year to execute OR TIME_UNUSED if this is a repetative crontab
266      * entry
267      * @param jobLabel - The label of the crontab entry.
268      * @param listener The class that gets called when the crontab 'rings'
269      * @throws CronException upon error
270      */

271     public CrontabEntry(int minute, int hour, int dayOfMonth, int month,
272                         int dayOfWeek, int year, String JavaDoc jobLabel, CrontabListenerI listener)
273             throws CronException {
274         setCounterValue();
275         this.minute = minute;
276         this.hour = hour;
277         this.dayOfMonth = dayOfMonth;
278         this.month = month;
279         this.dayOfWeek = dayOfWeek;
280         this.year = year;
281         this.listener = listener;
282         this.label = jobLabel;
283         isRepetitive = (year == TIME_UNUSED);
284         isRelative = false;
285         updateEntryTime();
286         checkEntryTime();
287
288         if (log.isDebugEnabled()) {
289             log.debug("Constructed new crontab entry: " + this.toString());
290         }
291     }
292
293     /**
294      * Get the alarm time in system time.
295      *
296      * @return long integer.
297      */

298     public long getAlarmTime() {
299         return alarmTime;
300     }
301
302     /**
303      * Retrieve an incrementing id value unique across crontab instances
304      *
305      * @return long value.
306      */

307     public long getCounter() {
308         return this.counterValue;
309     }
310
311     /**
312      * Retrieve the day of month setting
313      *
314      * @return integer
315      */

316     public int getDayOfMonth() {
317         return dayOfMonth;
318     }
319
320     /**
321      * Return day of week setting
322      *
323      * @return integer
324      */

325     public int getDayOfWeek() {
326         return dayOfWeek;
327     }
328
329     /**
330      * Retrieve hour setting
331      *
332      * @return integer
333      */

334     public int getHour() {
335         return hour;
336     }
337
338     /**
339      * Return isRelative value
340      *
341      * @return boolean
342      */

343     public boolean isIsRelative() {
344         return isRelative;
345     }
346
347     /**
348      * Retrieve the isRepetitive value
349      *
350      * @return boolean
351      */

352     public boolean isIsRepetitive() {
353         return isRepetitive;
354     }
355
356     /**
357      * Retrieve the label of the entry
358      *
359      * @return java.lang.String
360      */

361     public String JavaDoc getLabel() {
362         return label;
363     }
364
365     /**
366      * Retrieve the listener for the crontab.
367      *
368      * @return
369      */

370     public CrontabListenerI getListener() {
371         return this.listener;
372     }
373
374     /**
375      * Retrieve the minutes of the entry
376      *
377      * @return integer
378      */

379     public int getMinute() {
380         return minute;
381     }
382
383     /**
384      * Return the month value of the entry
385      *
386      * @return integer
387      */

388     public int getMonth() {
389         return month;
390     }
391
392     /**
393      * Return the year value of the entry
394      *
395      * @return integer
396      */

397     public int getYear() {
398         return year;
399     }
400
401     /**
402      * Standard comparison operator. Checks against the alarm entry time.
403      * <p/>
404      * <p/>
405      * compareTo and equals behave differently. compareTo only sorts by alarm
406      * time. Equals checks alarm times and listeners
407      * </p>
408      *
409      * @param obj The object to compare against.
410      * @return integer as per standard Object.compareTo values
411      * @see java.lang.Comparable#compareTo
412      */

413     public synchronized int compareTo(Object JavaDoc obj) {
414         CrontabEntry entry = (CrontabEntry) obj;
415
416         if (alarmTime < entry.alarmTime) {
417             return -1;
418         } else if (alarmTime > entry.alarmTime) {
419             return 1;
420         } else {
421             return (new Long JavaDoc(counterValue)).compareTo(new Long JavaDoc(entry.counterValue));
422         }
423     }
424
425     /**
426      * Returns true if the two alarm times of the comparing crontab entries are
427      * equal.
428      *
429      * @param obj The CrontabEntry to compare against.
430      * @return true if the two objects are equal
431      */

432     public synchronized boolean equals(Object JavaDoc obj) {
433         try {
434             CrontabEntry entry = (CrontabEntry) obj;
435
436             if ((alarmTime == entry.alarmTime) && (listener == entry.listener)) {
437                 return true;
438             }
439         } catch (ClassCastException JavaDoc cce) {
440             log.error("Class cast exception comparing objects", cce);
441         }
442
443         return false;
444     }
445
446
447     /**
448      * Returns the class as a string. Useful for debugging purposes
449      *
450      * @return a String representing this class.
451      */

452     public synchronized String JavaDoc toString() {
453         if (year != TIME_UNUSED) {
454             return "Cron Entry at " + new Date JavaDoc(alarmTime);
455         }
456
457         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("CronTabEntry params");
458         sb.append(" minute=");
459         sb.append(minute);
460         sb.append(" hour=");
461         sb.append(hour);
462         sb.append(" dayOfMonth=");
463         sb.append(dayOfMonth);
464         sb.append(" month=");
465         sb.append(month);
466         sb.append(" dayOfWeek=");
467         sb.append(dayOfWeek);
468         sb.append(" (next alarm date=" + new Date JavaDoc(alarmTime) + ")");
469
470         return sb.toString();
471     }
472
473     /**
474      * Set the new/latest time. For example, if it's a repeatable and the last
475      * alarm time is passed, then we calculate the next one.
476      */

477     public synchronized void updateEntryTime() {
478         if (isRelative) {
479             alarmTime = System.currentTimeMillis() + (minute * 60000);
480
481             return;
482         }
483
484         Calendar JavaDoc now = Calendar.getInstance();
485         Calendar JavaDoc alarm = (Calendar JavaDoc) now.clone();
486
487         if (log.isDebugEnabled()) {
488             log.debug("now: " + now.getTime());
489         }
490
491         if (year > TIME_UNUSED) {
492             alarm.set(Calendar.YEAR, year);
493         }
494
495         if (month > TIME_UNUSED) {
496             alarm.set(Calendar.MONTH, month);
497         }
498
499         if (hour > TIME_UNUSED) {
500             alarm.set(Calendar.HOUR_OF_DAY, hour);
501         }
502
503         if (minute > TIME_UNUSED) {
504             alarm.set(Calendar.MINUTE, minute);
505         }
506
507         alarm.set(Calendar.SECOND, 0);
508
509         //Now roll as necessary to make it the next proper execution time.
510
adjustExecutionTime(now, alarm);
511
512         if (log.isDebugEnabled()) {
513             log.debug("alarm: " + alarm.getTime());
514         }
515
516         alarmTime = alarm.getTime().getTime();
517     }
518
519     /**
520      * Sets the counter so that order is guaranteed
521      */

522     protected void setCounterValue() {
523         //
524
//We use an incremental counter to guarantee execution in the order
525
//of the original queue if times are equal
526
//
527
synchronized (counterLock) {
528             counterValue = globalCounter;
529             globalCounter++;
530         }
531     }
532
533     /**
534      * Adjusts the execution time so that the next execution time is proper
535      * ie.. all cron rules should be followed and the next cron execution
536      * should be greater than now.
537      *
538      * @param now 'now' calendar instance
539      * @param alarm the next instance that the calendar should execute.
540      */

541     protected synchronized void adjustExecutionTime(Calendar JavaDoc now, Calendar JavaDoc alarm) {
542         // for every minute alarm
543
if (minute == TIME_UNUSED) {
544             // Increments hour if now >= alarm (for every x minutes alarms)
545
if ((hour > TIME_UNUSED && now.get(Calendar.HOUR_OF_DAY) != hour) ||
546                     (dayOfMonth > TIME_UNUSED && now.get(Calendar.DAY_OF_MONTH) != dayOfMonth) ||
547                     (dayOfWeek > TIME_UNUSED && now.get(Calendar.DAY_OF_WEEK) != dayOfWeek)) {
548                 // if we specify a hour and it's not that hour, or doing a dayOfMonth or dayOfWeek and it's not that day,
549
// we want to set the minute to 0
550
alarm.set(Calendar.MINUTE, 0);
551             } else if ((hour <= TIME_UNUSED) && (now.get(Calendar.MINUTE) >= minute)) {
552                 // if we're not looking at the hour, we need to rollover to next hour if it's :59
553
alarm.add(Calendar.MINUTE, 1);
554             } else if ((hour > TIME_UNUSED) && (now.get(Calendar.MINUTE) >= minute) && (now.get(Calendar.MINUTE) == 59)) {
555                 // if we have a specific hour and it's the 59th minute, we need to roll the minute and day
556
alarm.roll(Calendar.MINUTE, true);
557                 alarm.roll(Calendar.DAY_OF_YEAR, true);
558             } else if (now.get(Calendar.MINUTE) >= minute) {
559                 // otherwise, increase minute (without increasing hour)
560
alarm.roll(Calendar.MINUTE, true);
561             }
562         }
563         
564         //For every hour alarm
565
if (minute != TIME_UNUSED && hour == TIME_UNUSED) {
566             // Increments hour if now >= alarm (for every x minutes alarms)
567
if ((dayOfMonth > TIME_UNUSED && now.get(Calendar.DAY_OF_MONTH) != dayOfMonth) ||
568                     (dayOfWeek > TIME_UNUSED && now.get(Calendar.DAY_OF_WEEK) != dayOfWeek)) {
569                 // if we're doing a dayOfMonth or dayOfWeek and it's not that day, we want to set the hour to midnight
570
alarm.set(Calendar.HOUR_OF_DAY, 0);
571             } else if ((dayOfMonth <= TIME_UNUSED) && (dayOfWeek <= TIME_UNUSED) && (now.get(Calendar.MINUTE) >= minute)) {
572                 // if we're not looking at the day, we need to rollover to tomorrow if it's 23:00
573
alarm.add(Calendar.HOUR_OF_DAY, 1);
574             } else if (now.get(Calendar.MINUTE) >= minute) {
575                 // otherwise, increase hour (without increasing day)
576
alarm.roll(Calendar.HOUR_OF_DAY, true);
577             }
578         }
579
580         // Increments hour if now >= alarm (for every x hours alarms)
581
// if (hour > TIME_UNUSED && minute > TIME_UNUSED) {
582
// if (now.get(Calendar.MINUTE) >= minute) //hour not increased during job
583
// alarm.add(Calendar.HOUR_OF_DAY, -hour);
584
// else
585
// alarm.add(Calendar.HOUR_OF_DAY, hour - 1);//assume hour increased with one
586
// }
587

588         // Incrementes dayOfYear if now >= alarm (for every x days alarms)
589
// if ((dayOfMonth <= TIME_UNUSED) && (dayOfWeek <= TIME_UNUSED) &&
590
// (hour > TIME_UNUSED) && (minute > TIME_UNUSED) &&
591
// ((now.get(Calendar.HOUR_OF_DAY) > hour) ||
592
// ((now.get(Calendar.HOUR_OF_DAY) == hour) &&
593
// (now.get(Calendar.MINUTE) >= minute)))) {
594
// alarm.roll(Calendar.DAY_OF_YEAR, true);
595
// }
596

597         if ((dayOfMonth <= TIME_UNUSED) && (dayOfWeek <= TIME_UNUSED) &&
598                 (((hour > TIME_UNUSED) && now.get(Calendar.HOUR_OF_DAY) > hour) ||
599                 ((minute > TIME_UNUSED) && (now.get(Calendar.HOUR_OF_DAY) == hour) &&
600                 (now.get(Calendar.MINUTE) >= minute)))) {
601             alarm.roll(Calendar.DAY_OF_YEAR, true);
602         }
603
604         // Incrementes year if now >= alarm (for monthly or yearly alarms)
605
if ((month != TIME_UNUSED) && (year == TIME_UNUSED) &&
606                 ((now.get(Calendar.MONTH) > month) ||
607                 ((now.get(Calendar.MONTH) == month) &&
608                 ((now.get(Calendar.DAY_OF_MONTH) > dayOfMonth) ||
609                 ((now.get(Calendar.DAY_OF_MONTH) == dayOfMonth) &&
610                 ((now.get(Calendar.HOUR_OF_DAY) > hour) ||
611                 ((now.get(Calendar.HOUR_OF_DAY) == hour) &&
612                 (now.get(Calendar.MINUTE) >= minute)))))))) {
613             alarm.add(Calendar.YEAR, 1);
614         }
615
616         if (dayOfWeek != TIME_UNUSED) { // Weekly alarms
617

618             int deltaOfDay = (7 + (dayOfWeek - now.get(Calendar.DAY_OF_WEEK))) % 7;
619
620             if (log.isDebugEnabled()) {
621                 log.debug("deltaOfDay: " + deltaOfDay);
622             }
623
624             if (deltaOfDay != 0) {
625                 alarm.add(Calendar.DAY_OF_YEAR, deltaOfDay);
626             } else if ((now.get(Calendar.HOUR_OF_DAY) > hour) ||
627                     ((now.get(Calendar.HOUR_OF_DAY) == hour) &&
628                     (now.get(Calendar.MINUTE) >= minute))) { // Incrementes week if now >= alarm
629
alarm.add(Calendar.WEEK_OF_YEAR, 1);
630             }
631         } else if (dayOfMonth != TIME_UNUSED) { // Monthly alarms
632
alarm.set(Calendar.DAY_OF_MONTH, dayOfMonth);
633
634             // Incrementes month if now >= alarm (for weekly alarms)
635
if ((month == TIME_UNUSED) &&
636                     ((now.get(Calendar.DAY_OF_MONTH) > dayOfMonth) ||
637                     ((now.get(Calendar.DAY_OF_MONTH) == dayOfMonth) &&
638                     ((now.get(Calendar.HOUR_OF_DAY) > hour) ||
639                     ((now.get(Calendar.HOUR_OF_DAY) == hour) &&
640                     (now.get(Calendar.MINUTE) >= minute)))))) {
641                 alarm.roll(Calendar.MONTH, true);
642             }
643         }
644     }
645
646     /**
647      * Checks the entry time.
648      *
649      * @throws CronException if delay is less than 1 second.
650      */

651     void checkEntryTime() throws CronException {
652         long delay = alarmTime - System.currentTimeMillis();
653
654         if (delay <= 1000) {
655             throw new CronException();
656         }
657     }
658
659     /**
660      * Return Job Number for this CrontabEntry's associated JobQueue entry
661      *
662      * @return Job Number for this CrontabEntry's associated JobQueue entry
663      */

664     public String JavaDoc getJobNumber() {
665         return jobNumber;
666     }
667
668     /**
669      * Set Job Number for this CrontabEntry's associated JobQueue entry
670      *
671      * @param s Job Number for this CrontabEntry's associated JobQueue entry
672      */

673     public void setJobNumber(String JavaDoc s) {
674         jobNumber = s;
675     }
676
677 }
678
Popular Tags