KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > quartz > NthIncludedDayTrigger


1 /*
2  * Copyright 2004-2005 OpenSymphony
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  *
16  */

17
18 package org.quartz;
19
20 import java.text.NumberFormat JavaDoc;
21 import java.util.Date JavaDoc;
22 import java.util.TimeZone JavaDoc;
23
24 /**
25  * A trigger which fires on the N<SUP>th</SUP> day of every interval type
26  * ({@link #INTERVAL_TYPE_WEEKLY}, {@link #INTERVAL_TYPE_MONTHLY} or
27  * {@link #INTERVAL_TYPE_YEARLY}) that is <i>not</i> excluded by the associated
28  * calendar. When determining what the N<SUP>th</SUP> day of the month or year
29  * is, <CODE>NthIncludedDayTrigger</CODE> will skip excluded days on the
30  * associated calendar. This would commonly be used in an N<SUP>th</SUP>
31  * business day situation, in which the user wishes to fire a particular job on
32  * the N<SUP>th</SUP> business day (i.e. the 5<SUP>th</SUP> business day of
33  * every month). Each <CODE>NthIncludedDayTrigger</CODE> also has an associated
34  * <CODE>fireAtTime</CODE> which indicates at what time of day the trigger is
35  * to fire.
36  * <P>
37  * All <CODE>NthIncludedDayTrigger</CODE>s default to a monthly interval type
38  * (fires on the N<SUP>th</SUP> day of every month) with N = 1 (first
39  * non-excluded day) and <CODE>fireAtTime</CODE> set to 12:00 PM (noon). These
40  * values can be changed using the {@link #setN}, {@link #setIntervalType}, and
41  * {@link #setFireAtTime} methods. Users may also want to note the
42  * {@link #setNextFireCutoffInterval} and {@link #getNextFireCutoffInterval}
43  * methods.
44  * <P>
45  * Take, for example, the following calendar:
46  * <P>
47  * <PRE>
48  * July August September
49  * Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
50  * 1 W 1 2 3 4 5 W 1 2 W
51  * W H 5 6 7 8 W W 8 9 10 11 12 W W H 6 7 8 9 W
52  * W 11 12 13 14 15 W W 15 16 17 18 19 W W 12 13 14 15 16 W
53  * W 18 19 20 21 22 W W 22 23 24 25 26 W W 19 20 21 22 23 W
54  * W 25 26 27 28 29 W W 29 30 31 W 26 27 28 29 30
55  * W
56  * </PRE>
57  * <P>
58  * Where W's represent weekend days, and H's represent holidays, all of which
59  * are excluded on a calendar associated with an
60  * <CODE>NthIncludedDayTrigger</CODE> with <CODE>n=5</CODE> and
61  * <CODE>intervalType=INTERVAL_TYPE_MONTHLY</CODE>. In this case, the trigger
62  * would fire on the 8<SUP>th</SUP> of July (because of the July 4 holiday),
63  * the 5<SUP>th</SUP> of August, and the 8<SUP>th</SUP> of September (because
64  * of Labor Day).
65  *
66  * @author Aaron Craven
67  */

68 public class NthIncludedDayTrigger extends Trigger {
69
70     static final long serialVersionUID = 6267700049629328293L;
71     
72     /**
73      * Instructs the <CODE>Scheduler</CODE> that upon a mis-fire situation, the
74      * <CODE>NthIncludedDayTrigger</CODE> wants to be fired now by the
75      * <CODE>Scheduler</CODE>
76      */

77     public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
78
79     /**
80      * Instructs the <CODE>Scheduler</CODE> that upon a mis-fire situation, the
81      * <CODE>NthIncludedDayTrigger</CODE> wants to have
82      * <CODE>nextFireTime</CODE> updated to the next time in the schedule after
83      * the current time, but it does not want to be fired now.
84      */

85     public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
86
87     /**
88      * indicates a monthly trigger type (fires on the N<SUP>th</SUP> included
89      * day of every month).
90      */

91     public static final int INTERVAL_TYPE_MONTHLY = 1;
92     
93     /**
94      * indicates a yearly trigger type (fires on the N<SUP>th</SUP> included
95      * day of every year).
96      */

97     public static final int INTERVAL_TYPE_YEARLY = 2;
98     
99     /**
100      * indicates a weekly trigger type (fires on the N<SUP>th</SUP> included
101      * day of every week). When using this interval type, care must be taken
102      * not to think of the value of <CODE>n</CODE> as an analog to
103      * <CODE>java.util.Calendar.DAY_OF_WEEK</CODE>. Such a comparison can only
104      * be drawn when there are no calendars associated with the trigger. To
105      * illustrate, consider an <CODE>NthIncludedDayTrigger</CODE> with
106      * <CODE>n = 3</CODE> which is associated with a Calendar excluding
107      * non-weekdays. The trigger would fire on the 3<SUP>rd</SUP>
108      * <I>included</I> day of the week, which would be 4<SUP>th</SUP>
109      * <I>actual</I> day of the week.
110      */

111     public static final int INTERVAL_TYPE_WEEKLY = 3;
112     
113     private Date JavaDoc startTime = new Date JavaDoc();
114     private Date JavaDoc endTime;
115     private Date JavaDoc previousFireTime;
116     private Date JavaDoc nextFireTime;
117     private Calendar calendar;
118     
119     private int n = 1;
120     private int intervalType = INTERVAL_TYPE_MONTHLY;
121     private int fireAtHour = 12;
122     private int fireAtMinute = 0;
123     private int fireAtSecond = 0;
124     private int nextFireCutoffInterval = 12;
125     
126     private TimeZone JavaDoc timeZone;
127     
128     /**
129      * Create an <CODE>NthIncludedDayTrigger</CODE> with no specified name,
130      * group, or <CODE>JobDetail</CODE>. This will result initially in a
131      * default monthly trigger that fires on the first day of every month at
132      * 12:00 PM (<CODE>n</CODE>=1,
133      * <CODE>intervalType={@link #INTERVAL_TYPE_MONTHLY}</CODE>,
134      * <CODE>fireAtTime="12:00"</CODE>).
135      * <P>
136      * Note that <CODE>setName()</CODE>, <CODE>setGroup()</CODE>,
137      * <CODE>setJobName()</CODE>, and <CODE>setJobGroup()</CODE>, must be
138      * called before the <CODE>NthIncludedDayTrigger</CODE> can be placed into
139      * a <CODE>Scheduler</CODE>.
140      */

141     public NthIncludedDayTrigger() {
142         super();
143     }
144
145     /**
146      * Create an <CODE>NthIncludedDayTrigger</CODE> with the given name and
147      * group but no specified <CODE>JobDetail</CODE>. This will result
148      * initially in a default monthly trigger that fires on the first day of
149      * every month at 12:00 PM (<CODE>n</CODE>=1,
150      * <CODE>intervalType={@link #INTERVAL_TYPE_MONTHLY}</CODE>,
151      * <CODE>fireAtTime="12:00"</CODE>).
152      * <P>
153      * Note that <CODE>setJobName()</CODE> and <CODE>setJobGroup()</CODE> must
154      * be called before the <CODE>NthIncludedDayTrigger</CODE> can be placed
155      * into a <CODE>Scheduler</CODE>.
156      *
157      * @param name the name for the <CODE>NthIncludedDayTrigger</CODE>
158      * @param group the group for the <CODE>NthIncludedDayTrigger</CODE>
159      */

160     public NthIncludedDayTrigger(String JavaDoc name, String JavaDoc group) {
161         super(name, group);
162     }
163
164     /**
165      * Create an <CODE>NthIncludedDayTrigger</CODE> with the given name and
166      * group and the specified <CODE>JobDetail</CODE>. This will result
167      * initially in a default monthly trigger that fires on the first day of
168      * every month at 12:00 PM (<CODE>n</CODE>=1,
169      * <CODE>intervalType={@link #INTERVAL_TYPE_MONTHLY}</CODE>,
170      * <CODE>fireAtTime="12:00"</CODE>).
171      *
172      * @param name the name for the <CODE>NthIncludedDayTrigger</CODE>
173      * @param group the group for the <CODE>NthIncludedDayTrigger</CODE>
174      * @param jobName the name of the job to associate with the
175      * <CODE>NthIncludedDayTrigger</CODE>
176      * @param jobGroup the group containing the job to associate with the
177      * <CODE>NthIncludedDayTrigger</CODE>
178      */

179     public NthIncludedDayTrigger(String JavaDoc name, String JavaDoc group, String JavaDoc jobName,
180             String JavaDoc jobGroup) {
181         super(name, group, jobName, jobGroup);
182     }
183
184     /**
185      * Sets the day of the interval on which the
186      * <CODE>NthIncludedDayTrigger</CODE> should fire. If the N<SUP>th</SUP>
187      * day of the interval does not exist (i.e. the 32<SUP>nd</SUP> of a
188      * month), the trigger simply will never fire. N may not be less than 1.
189      *
190      * @param n the day of the interval on which the trigger should fire.
191      * @throws java.lang.IllegalArgumentException
192      * the value entered for N was not valid (probably less than or
193      * equal to zero).
194      * @see #getN()
195      */

196     public void setN(int n) {
197         if (n > 0) {
198             this.n = n;
199         } else {
200             throw new IllegalArgumentException JavaDoc("N must be greater than 0.");
201         }
202     }
203     
204     /**
205      * Returns the day of the interval on which the
206      * <CODE>NthIncludedDayTrigger</CODE> should fire.
207      *
208      * @return the value of <CODE>n</CODE>
209      * @see #setN(int)
210      */

211     public int getN() {
212         return this.n;
213     }
214     
215     /**
216      * Sets the interval type for the <CODE>NthIncludedDayTrigger</CODE>. If
217      * {@link #INTERVAL_TYPE_MONTHLY}, the trigger will fire on the
218      * N<SUP>th</SUP> included day of every month. If
219      * {@link #INTERVAL_TYPE_YEARLY}, the trigger will fire on the
220      * N<SUP>th</SUP> included day of every year. If
221      * {@link #INTERVAL_TYPE_WEEKLY}, the trigger will fire on the
222      * N<SUP>th</SUP> included day of every week.
223      *
224      * @param intervalType the interval type for the trigger
225      * @throws java.lang.IllegalArgumentException
226      * the value of <CODE>intervalType</CODE> is not valid. Valid
227      * values are represented by the INTERVAL_TYPE_WEEKLY,
228      * INTERVAL_TYPE_MONTHLY and INTERVAL_TYPE_YEARLY constants.
229      * @see #getIntervalType()
230      * @see #INTERVAL_TYPE_WEEKLY
231      * @see #INTERVAL_TYPE_MONTHLY
232      * @see #INTERVAL_TYPE_YEARLY
233      */

234     public void setIntervalType(int intervalType) {
235         switch (intervalType) {
236             case INTERVAL_TYPE_WEEKLY:
237                 this.intervalType = intervalType;
238                 break;
239             case INTERVAL_TYPE_MONTHLY:
240                 this.intervalType = intervalType;
241                 break;
242             case INTERVAL_TYPE_YEARLY:
243                 this.intervalType = intervalType;
244                 break;
245             default:
246                 throw new IllegalArgumentException JavaDoc("Invalid Interval Type:"
247                                                + intervalType);
248         }
249     }
250     
251     /**
252      * Returns the interval type for the <CODE>NthIncludedDayTrigger</CODE>.
253      *
254      * @return the trigger's interval type
255      * @see #setIntervalType(int)
256      * @see #INTERVAL_TYPE_WEEKLY
257      * @see #INTERVAL_TYPE_MONTHLY
258      * @see #INTERVAL_TYPE_YEARLY
259      */

260     public int getIntervalType() {
261         return this.intervalType;
262     }
263     
264     /**
265      * Sets the fire time for the <CODE>NthIncludedDayTrigger</CODE>, which
266      * should be represented as a string with the format
267      * &quot;HH:MM[:SS]&quot;, with HH representing the 24-hour clock hour
268      * of the fire time. Hours can be represented as either a one-digit or
269      * two-digit number. Seconds are optional.
270      *
271      * @param fireAtTime the time at which the trigger should fire
272      * @throws java.lang.IllegalArgumentException
273      * the specified value for <CODE>fireAtTime</CODE> could not be
274      * successfully parsed into a valid time of day.
275      * @see #getFireAtTime()
276      */

277     public void setFireAtTime(String JavaDoc fireAtTime) {
278         int newFireHour;
279         int newFireMinute;
280         int newFireSecond = 0;
281         
282         try {
283             int i = fireAtTime.indexOf(":");
284             newFireHour = Integer.parseInt(fireAtTime.substring(0, i));
285             newFireMinute = Integer.parseInt(fireAtTime.substring(i+1, i+3));
286             i = fireAtTime.indexOf(":", i+1);
287             if (i > -1) {
288                 newFireSecond = Integer.parseInt(fireAtTime.substring(i+1));
289             }
290         } catch (Exception JavaDoc e) {
291             throw new IllegalArgumentException JavaDoc(
292                 "Could not parse time expression '" + fireAtTime + "':" + e.getMessage());
293         }
294         
295         // Check ranges
296
if ((newFireHour < 0) || (newFireHour > 23)) {
297             throw new IllegalArgumentException JavaDoc(
298                 "Could not parse time expression '" + fireAtTime + "':" +
299                 "fireAtHour must be between 0 and 23");
300         } else if ((newFireMinute < 0) || (newFireMinute > 59)) {
301             throw new IllegalArgumentException JavaDoc(
302                 "Could not parse time expression '" + fireAtTime + "':" +
303                 "fireAtMinute must be between 0 and 59");
304         } else if ((newFireSecond < 0) || (newFireSecond > 59)) {
305             throw new IllegalArgumentException JavaDoc(
306                 "Could not parse time expression '" + fireAtTime + "':" +
307                 "fireAtMinute must be between 0 and 59");
308         }
309         
310         fireAtHour = newFireHour;
311         fireAtMinute = newFireMinute;
312         fireAtSecond = newFireSecond;
313     }
314     
315     /**
316      * Returns the fire time for the <CODE>NthIncludedDayTrigger</CODE> as a
317      * string with the format &quot;HH:MM&quot;, with HH representing the
318      * 24-hour clock hour of the fire time.
319      *
320      * @return the fire time for the trigger
321      * @see #setFireAtTime(String)
322      */

323     public String JavaDoc getFireAtTime() {
324         NumberFormat JavaDoc format = NumberFormat.getNumberInstance();
325         format.setMaximumIntegerDigits(2);
326         format.setMinimumIntegerDigits(2);
327         format.setMaximumFractionDigits(0);
328         
329         return format.format(this.fireAtHour) + ":" +
330                format.format(this.fireAtMinute) + ":" +
331                format.format(this.fireAtSecond);
332     }
333
334     /**
335      * Sets the <CODE>nextFireCutoffInterval</CODE> for the
336      * <CODE>NthIncludedDayTrigger</CODE>.
337      * <P>
338      * Because of the conceptual design of <CODE>NthIncludedDayTrigger</CODE>,
339      * it is not always possible to decide with certainty that the trigger
340      * will <I>never</I> fire again. Therefore, it will search for the next
341      * fire time up to a given cutoff. These cutoffs can be changed by using the
342      * {@link #setNextFireCutoffInterval(int)} and
343      * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
344      * of the intervals specified by <CODE>{@link #getIntervalType()
345      * intervalType}</CODE>.
346      * <P>
347      * In most cases, the default value of this setting (12) is sufficient (it
348      * is highly unlikely, for example, that you will need to look at more than
349      * 12 months of dates to ensure that your trigger will never fire again).
350      * However, this setting is included to allow for the rare exceptions where
351      * this might not be true.
352      * <P>
353      * For example, if your trigger is associated with a calendar that excludes
354      * a great many dates in the next 12 months, and hardly any following that,
355      * it is possible (if <CODE>n</CODE> is large enough) that you could run
356      * into this situation.
357      *
358      * @param nextFireCutoffInterval the desired cutoff interval
359      * @see #getNextFireCutoffInterval()
360      * @see #getNextFireTime()
361      */

362     public void setNextFireCutoffInterval(int nextFireCutoffInterval) {
363         this.nextFireCutoffInterval = nextFireCutoffInterval;
364     }
365     
366     /**
367      * Returns the <CODE>nextFireCutoffInterval</CODE> for the
368      * <CODE>NthIncludedDayTrigger</CODE>.
369      * <P>
370      * Because of the conceptual design of <CODE>NthIncludedDayTrigger</CODE>,
371      * it is not always possible to decide with certainty that the trigger
372      * will <I>never</I> fire again. Therefore, it will search for the next
373      * fire time up to a given cutoff. These cutoffs can be changed by using the
374      * {@link #setNextFireCutoffInterval(int)} and
375      * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
376      * of the intervals specified by <CODE>{@link #getIntervalType()
377      * intervalType}</CODE>.
378      *
379      * @return the chosen cutoff interval
380      * @see #setNextFireCutoffInterval(int)
381      * @see #getNextFireTime()
382      */

383     public int getNextFireCutoffInterval() {
384         return this.nextFireCutoffInterval;
385     }
386     
387     /**
388      * Sets the date/time on which the trigger may begin firing. This defines
389      * the initial boundary for trigger firings &mdash; the trigger will not
390      * fire prior to this date and time. Defaults to the current date and time
391      * when the <CODE>NthIncludedDayTrigger</CODE> is created.
392      *
393      * @param startTime the initial boundary for trigger firings
394      * @throws java.lang.IllegalArgumentException
395      * the specified start time is after the current end time or is
396      * null
397      * @see #getStartTime()
398      */

399     public void setStartTime(Date JavaDoc startTime) {
400         if (startTime == null) {
401             throw new IllegalArgumentException JavaDoc("Start time may not be null");
402         }
403         if ((this.endTime != null) && endTime.before(startTime)) {
404             throw new IllegalArgumentException JavaDoc("Start time must be before end time.");
405         }
406         this.startTime = startTime;
407     }
408
409     /**
410      * Returns the date/time on which the trigger may begin firing. This
411      * defines the initial boundary for trigger firings &mdash; the trigger
412      * will not fire prior to this date and time.
413      *
414      * @return the initial date/time on which the trigger may begin firing
415      * @see #setStartTime(Date)
416      */

417     public Date JavaDoc getStartTime() {
418         return this.startTime;
419     }
420
421     /**
422      * Sets the date/time on which the trigger must stop firing. This defines
423      * the final boundary for trigger firings &mdash; the trigger will not
424      * fire after to this date and time. If this value is null, no end time
425      * boundary is assumed, and the trigger can continue indefinitely.
426      *
427      * @param endTime the final boundary for trigger firings
428      * @throws java.lang.IllegalArgumentException
429      * the specified end time is before the current start time
430      * @see #getEndTime()
431      */

432     public void setEndTime(Date JavaDoc endTime) {
433         if ((endTime != null) && endTime.before(startTime)) {
434             throw new IllegalArgumentException JavaDoc("End time must be after start time.");
435         }
436         this.endTime = endTime;
437     }
438
439     /**
440      * Returns the date/time on which the trigger must stop firing. This
441      * defines the final boundary for trigger firings &mdash; the trigger will
442      * not fire after to this date and time. If this value is null, no end time
443      * boundary is assumed, and the trigger can continue indefinitely.
444      *
445      * @return the date/time on which the trigger must stop firing
446      * @see #setEndTime(Date)
447      */

448     public Date JavaDoc getEndTime() {
449         return this.endTime;
450     }
451
452     /**
453      * Sets the time zone in which the <code>fireAtTime</code> will be resolved.
454      * If no time zone is provided, then the default time zone will be used.
455      *
456      * @see TimeZone#getDefault()
457      * @see #getTimeZone()
458      * @see #getFireAtTime()
459      * @see #setFireAtTime(String)
460      */

461     public void setTimeZone(TimeZone JavaDoc timeZone) {
462         this.timeZone = timeZone;
463     }
464     
465     /**
466      * Gets the time zone in which the <code>fireAtTime</code> will be resolved.
467      * If no time zone was explicitly set, then the default time zone is used.
468      *
469      * @see TimeZone#getDefault()
470      * @see #getTimeZone()
471      * @see #getFireAtTime()
472      * @see #setFireAtTime(String)
473      */

474     public TimeZone JavaDoc getTimeZone() {
475         if (timeZone == null) {
476             timeZone = TimeZone.getDefault();
477         }
478         return timeZone;
479     }
480     
481     
482     /**
483      * Returns the next time at which the <CODE>NthIncludedDayTrigger</CODE>
484      * will fire. If the trigger will not fire again, <CODE>null</CODE> will be
485      * returned.
486      * <P>
487      * Because of the conceptual design of <CODE>NthIncludedDayTrigger</CODE>,
488      * it is not always possible to decide with certainty that the trigger
489      * will <I>never</I> fire again. Therefore, it will search for the next
490      * fire time up to a given cutoff. These cutoffs can be changed by using the
491      * {@link #setNextFireCutoffInterval(int)} and
492      * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
493      * of the intervals specified by <CODE>{@link #getIntervalType()
494      * intervalType}</CODE>.
495      * <P>
496      * The returned value is not guaranteed to be valid until after
497      * the trigger has been added to the scheduler.
498      *
499      * @return the next fire time for the trigger
500      * @see #getNextFireCutoffInterval()
501      * @see #setNextFireCutoffInterval(int)
502      * @see #getFireTimeAfter(Date)
503      */

504     public Date JavaDoc getNextFireTime() {
505         return this.nextFireTime;
506     }
507
508     /**
509      * Returns the previous time at which the
510      * <CODE>NthIncludedDayTrigger</CODE> fired. If the trigger has not yet
511      * fired, <CODE>null</CODE> will be returned.
512      *
513      * @return the previous fire time for the trigger
514      */

515     public Date JavaDoc getPreviousFireTime() {
516         return this.previousFireTime;
517     }
518
519     /**
520      * Returns the first time the <CODE>NthIncludedDayTrigger</CODE> will fire
521      * after the specified date.
522      * <P>
523      * Because of the conceptual design of <CODE>NthIncludedDayTrigger</CODE>,
524      * it is not always possible to decide with certainty that the trigger
525      * will <I>never</I> fire again. Therefore, it will search for the next
526      * fire time up to a given cutoff. These cutoffs can be changed by using the
527      * {@link #setNextFireCutoffInterval(int)} and
528      * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
529      * of the intervals specified by <CODE>{@link #getIntervalType()
530      * intervalType}</CODE>.
531      * <P>
532      * Therefore, for triggers with <CODE>intervalType =
533      * {@link NthIncludedDayTrigger#INTERVAL_TYPE_WEEKLY
534      * INTERVAL_TYPE_WEEKLY}</CODE>, if the trigger will not fire within 12
535      * weeks after the given date/time, <CODE>null</CODE> will be returned. For
536      * triggers with <CODE>intervalType =
537      * {@link NthIncludedDayTrigger#INTERVAL_TYPE_MONTHLY
538      * INTERVAL_TYPE_MONTHLY}</CODE>, if the trigger will not fire within 12
539      * months after the given date/time, <CODE>null</CODE> will be returned.
540      * For triggers with <CODE>intervalType =
541      * {@link NthIncludedDayTrigger#INTERVAL_TYPE_YEARLY
542      * INTERVAL_TYPE_YEARLY}</CODE>, if the trigger will not fire within 12
543      * years after the given date/time, <CODE>null</CODE> will be returned. In
544      * all cases, if the trigger will not fire before <CODE>endTime</CODE>,
545      * <CODE>null</CODE> will be returned.
546      *
547      * @param afterTime The time after which to find the nearest fire time.
548      * This argument is treated as exclusive &mdash; that is,
549      * if afterTime is a valid fire time for the trigger, it
550      * will not be returned as the next fire time.
551      * @return the first time the trigger will fire following the specified
552      * date
553      */

554     public Date JavaDoc getFireTimeAfter(Date JavaDoc afterTime) {
555         if (afterTime == null) {
556             afterTime = new Date JavaDoc();
557         }
558         
559         if (afterTime.before(this.startTime)) {
560             afterTime = new Date JavaDoc(startTime.getTime() - 1000l);
561         }
562         
563         if (this.intervalType == INTERVAL_TYPE_WEEKLY) {
564             return getWeeklyFireTimeAfter(afterTime);
565         } else if (this.intervalType == INTERVAL_TYPE_MONTHLY) {
566             return getMonthlyFireTimeAfter(afterTime);
567         } else if (this.intervalType == INTERVAL_TYPE_YEARLY) {
568             return getYearlyFireTimeAfter(afterTime);
569         } else {
570             return null;
571         }
572     }
573
574     /**
575      * Returns the last time the <CODE>NthIncludedDayTrigger</CODE> will fire.
576      * If the trigger will not fire at any point between <CODE>startTime</CODE>
577      * and <CODE>endTime</CODE>, <CODE>null</CODE> will be returned.
578      *
579      * @return the last time the trigger will fire.
580      */

581     public Date JavaDoc getFinalFireTime() {
582         Date JavaDoc finalTime = null;
583         java.util.Calendar JavaDoc currCal = java.util.Calendar.getInstance();
584         currCal.setTime(this.endTime);
585         
586         while ((finalTime == null)
587                 && (this.startTime.before(currCal.getTime()))) {
588             currCal.add(java.util.Calendar.DATE, -1);
589             
590             finalTime = getFireTimeAfter(currCal.getTime());
591         }
592         
593         return finalTime;
594     }
595
596     /**
597      * Called when the <CODE>Scheduler</CODE> has decided to 'fire' the trigger
598      * (execute the associated <CODE>Job</CODE>), in order to give the
599      * <CODE>Trigger</CODE> a chance to update itself for its next triggering
600      * (if any).
601      */

602     public void triggered(Calendar calendar) {
603         this.calendar = calendar;
604         this.previousFireTime = this.nextFireTime;
605         this.nextFireTime = getFireTimeAfter(this.nextFireTime);
606     }
607     
608     /**
609      * Called by the scheduler at the time a <CODE>Trigger</code> is first
610      * added to the scheduler, in order to have the <CODE>Trigger</CODE>
611      * compute its first fire time, based on any associated calendar.
612      * <P>
613      * After this method has been called, <CODE>getNextFireTime()</CODE>
614      * should return a valid answer.
615      * </p>
616      *
617      * @return the first time at which the <CODE>Trigger</CODE> will be fired
618      * by the scheduler, which is also the same value
619      * {@link #getNextFireTime()} will return (until after the first
620      * firing of the <CODE>Trigger</CODE>).
621      */

622     public Date JavaDoc computeFirstFireTime(Calendar calendar) {
623         this.calendar = calendar;
624         this.nextFireTime =
625             getFireTimeAfter(new Date JavaDoc(this.startTime.getTime() - 1000l));
626         
627         return this.nextFireTime;
628     }
629
630     /**
631     * Called after the <CODE>Scheduler</CODE> has executed the
632     * <code>JobDetail</CODE> associated with the <CODE>Trigger</CODE> in order
633     * to get the final instruction code from the trigger.
634     *
635     * @param jobCtx the <CODE>JobExecutionContext</CODE> that was used by the
636     * <CODE>Job</CODE>'s <CODE>execute()</CODE> method.
637     * @param result the <CODE>JobExecutionException</CODE> thrown by the
638     * <CODE>Job</CODE>, if any (may be <CODE>null</CODE>)
639     * @return one of the Trigger.INSTRUCTION_XXX constants.
640     */

641     public int executionComplete(JobExecutionContext jobCtx,
642         JobExecutionException result) {
643         if (result != null && result.refireImmediately()) {
644             return INSTRUCTION_RE_EXECUTE_JOB;
645         }
646
647         if (result != null && result.unscheduleFiringTrigger()) {
648             return INSTRUCTION_SET_TRIGGER_COMPLETE;
649         }
650     
651         if (result != null && result.unscheduleAllTriggers()) {
652             return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
653         }
654     
655         if (!mayFireAgain()) {
656             return INSTRUCTION_DELETE_TRIGGER;
657         }
658     
659         return INSTRUCTION_NOOP;
660     }
661
662     /**
663     * Used by the <CODE>Scheduler</CODE> to determine whether or not it is
664     * possible for this <CODE>Trigger</CODE> to fire again.
665     * <P>
666     * If the returned value is <CODE>false</CODE> then the
667     * <CODE>Scheduler</CODE> may remove the <CODE>Trigger</CODE> from the
668     * <CODE>JobStore</CODE>
669     *
670     * @return a boolean indicator of whether the trigger could potentially fire
671     * again
672     */

673     public boolean mayFireAgain() {
674         return (getNextFireTime() != null);
675     }
676
677     /**
678      * Indicates whether <CODE>misfireInstruction</CODE> is a valid misfire
679      * instruction for this <CODE>Trigger</CODE>.
680      *
681      * @return whether <CODE>misfireInstruction</CODE> is valid.
682      */

683     protected boolean validateMisfireInstruction(int misfireInstruction) {
684         if ((misfireInstruction == MISFIRE_INSTRUCTION_SMART_POLICY) ||
685                 (misfireInstruction == MISFIRE_INSTRUCTION_DO_NOTHING) ||
686                 (misfireInstruction == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW)) {
687             return true;
688         } else {
689             return false;
690         }
691     }
692
693     /**
694      * Updates the <CODE>NthIncludedDayTrigger</CODE>'s state based on the
695      * MISFIRE_INSTRUCTION_XXX that was selected when the
696      * <CODE>NthIncludedDayTrigger</CODE> was created
697      * <P>
698      * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
699      * then the instruction will be interpreted as
700      * {@link #MISFIRE_INSTRUCTION_FIRE_ONCE_NOW}.
701      *
702      * @param calendar a new or updated calendar to use for the trigger
703      */

704     public void updateAfterMisfire(Calendar calendar) {
705         int instruction = getMisfireInstruction();
706         
707         this.calendar = calendar;
708         
709         if (instruction == MISFIRE_INSTRUCTION_SMART_POLICY) {
710             instruction = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
711         }
712         
713         if (instruction == MISFIRE_INSTRUCTION_DO_NOTHING) {
714             this.nextFireTime = getFireTimeAfter(new Date JavaDoc());
715         } else if (instruction == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
716             this.nextFireTime = new Date JavaDoc();
717         }
718     }
719
720     /**
721      * Updates the <CODE>NthIncludedDayTrigger</CODE>'s state based on the
722      * given new version of the associated <CODE>Calendar</CODE>.
723      *
724      * @param calendar a new or updated calendar to use for the trigger
725      * @param misfireThreshold the amount of time (in milliseconds) that must
726      * be between &quot;now&quot; and the time the next
727      * firing of the trigger is supposed to occur.
728      */

729     public void updateWithNewCalendar(Calendar calendar,
730             long misfireThreshold) {
731         Date JavaDoc now = new Date JavaDoc();
732         long diff;
733         
734         this.calendar = calendar;
735         this.nextFireTime = getFireTimeAfter(this.previousFireTime);
736         
737         if ((this.nextFireTime != null) && (this.nextFireTime.before(now))) {
738             diff = now.getTime() - this.nextFireTime.getTime();
739             if (diff >= misfireThreshold) {
740                 this.nextFireTime = getFireTimeAfter(this.nextFireTime);
741             }
742         }
743     }
744
745     /**
746      * Calculates the first time an <CODE>NthIncludedDayTrigger</CODE> with
747      * <CODE>intervalType = {@link #INTERVAL_TYPE_WEEKLY}</CODE> will fire
748      * after the specified date. See {@link #getNextFireTime} for more
749      * information.
750      *
751      * @param afterDate The time after which to find the nearest fire time.
752      * This argument is treated as exclusive &mdash; that is,
753      * if afterTime is a valid fire time for the trigger, it
754      * will not be returned as the next fire time.
755      * @return the first time the trigger will fire following the specified
756      * date
757      */

758     private Date JavaDoc getWeeklyFireTimeAfter(Date JavaDoc afterDate) {
759         int currN = 0;
760         java.util.Calendar JavaDoc afterCal;
761         java.util.Calendar JavaDoc currCal;
762         int currWeek;
763         int weekCount = 0;
764         boolean gotOne = false;
765         
766         afterCal = java.util.Calendar.getInstance(getTimeZone());
767         afterCal.setTime(afterDate);
768         
769         currCal = java.util.Calendar.getInstance(getTimeZone());
770         currCal.set(afterCal.get(java.util.Calendar.YEAR),
771                     afterCal.get(java.util.Calendar.MONTH),
772                     afterCal.get(java.util.Calendar.DAY_OF_MONTH));
773
774         //move to the first day of the week (SUNDAY)
775
currCal.add(java.util.Calendar.DAY_OF_MONTH,
776                 (afterCal.get(java.util.Calendar.DAY_OF_WEEK) - 1) * -1);
777
778         currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
779         currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
780         currCal.set(java.util.Calendar.SECOND, this.fireAtSecond);
781         currCal.set(java.util.Calendar.MILLISECOND, 0);
782         
783         currWeek = currCal.get(java.util.Calendar.WEEK_OF_YEAR);
784         
785         while ((!gotOne) && (weekCount < this.nextFireCutoffInterval)) {
786             while ((currN != this.n) && (weekCount < 12)) {
787                 //if we move into a new week, reset the current "n" counter
788
if (currCal.get(java.util.Calendar.WEEK_OF_YEAR) != currWeek) {
789                     currN = 0;
790                     weekCount++;
791                     currWeek = currCal.get(java.util.Calendar.WEEK_OF_YEAR);
792                 }
793                 
794                 //treating a null calendar as an all-inclusive calendar,
795
// increment currN if the current date being tested is included
796
// on the calendar
797
if ((calendar == null)
798                         || (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
799                     currN++;
800                 }
801
802                 if (currN != this.n) {
803                     currCal.add(java.util.Calendar.DATE, 1);
804                 }
805                 
806                 //if we pass endTime, drop out and return null.
807
if ((this.endTime != null)
808                         && (currCal.getTime().after(this.endTime))) {
809                     return null;
810                 }
811             }
812             
813             //We found an "n" or we've checked the requisite number of weeks.
814
// If we've found an "n", is it the right one? -- that is, we could
815
// be looking at an nth day PRIOR to afterDate
816
if (currN == this.n) {
817                 if (afterDate.before(currCal.getTime())) {
818                     gotOne = true;
819                 } else { //resume checking on the first day of the next week
820
currCal.add(java.util.Calendar.DAY_OF_MONTH, -1 * (currN - 1));
821                     currCal.add(java.util.Calendar.DAY_OF_MONTH, 7);
822                     currN = 0;
823                 }
824             }
825         }
826         
827         if (weekCount < this.nextFireCutoffInterval) {
828             return currCal.getTime();
829         } else {
830             return null;
831         }
832     }
833
834     /**
835      * Calculates the first time an <CODE>NthIncludedDayTrigger</CODE> with
836      * <CODE>intervalType = {@link #INTERVAL_TYPE_MONTHLY}</CODE> will fire
837      * after the specified date. See {@link #getNextFireTime} for more
838      * information.
839      *
840      * @param afterDate The time after which to find the nearest fire time.
841      * This argument is treated as exclusive &mdash; that is,
842      * if afterTime is a valid fire time for the trigger, it
843      * will not be returned as the next fire time.
844      * @return the first time the trigger will fire following the specified
845      * date
846      */

847     private Date JavaDoc getMonthlyFireTimeAfter(Date JavaDoc afterDate) {
848         int currN = 0;
849         java.util.Calendar JavaDoc afterCal;
850         java.util.Calendar JavaDoc currCal;
851         int currMonth;
852         int monthCount = 0;
853         boolean gotOne = false;
854         
855         afterCal = java.util.Calendar.getInstance(getTimeZone());
856         afterCal.setTime(afterDate);
857         
858         currCal = java.util.Calendar.getInstance(getTimeZone());
859         currCal.set(afterCal.get(java.util.Calendar.YEAR),
860                     afterCal.get(java.util.Calendar.MONTH), 1);
861         currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
862         currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
863         currCal.set(java.util.Calendar.SECOND, this.fireAtSecond);
864         currCal.set(java.util.Calendar.MILLISECOND, 0);
865         
866         currMonth = currCal.get(java.util.Calendar.MONTH);
867         
868         while ((!gotOne) && (monthCount < this.nextFireCutoffInterval)) {
869             while ((currN != this.n) && (monthCount < 12)) {
870                 //if we move into a new month, reset the current "n" counter
871
if (currCal.get(java.util.Calendar.MONTH) != currMonth) {
872                     currN = 0;
873                     monthCount++;
874                     currMonth = currCal.get(java.util.Calendar.MONTH);
875                 }
876                 
877                 //treating a null calendar as an all-inclusive calendar,
878
// increment currN if the current date being tested is included
879
// on the calendar
880
if ((calendar == null)
881                         || (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
882                     currN++;
883                 }
884
885                 if (currN != this.n) {
886                     currCal.add(java.util.Calendar.DATE, 1);
887                 }
888                 
889                 //if we pass endTime, drop out and return null.
890
if ((this.endTime != null)
891                         && (currCal.getTime().after(this.endTime))) {
892                     return null;
893                 }
894             }
895             
896             //We found an "n" or we've checked the requisite number of months.
897
// If we've found an "n", is it the right one? -- that is, we could
898
// be looking at an nth day PRIOR to afterDate
899
if (currN == this.n) {
900                 if (afterDate.before(currCal.getTime())) {
901                     gotOne = true;
902                 } else { //resume checking on the first day of the next month
903
currCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
904                     currCal.add(java.util.Calendar.MONTH, 1);
905                     currN = 0;
906                 }
907             }
908         }
909         
910         if (monthCount < this.nextFireCutoffInterval) {
911             return currCal.getTime();
912         } else {
913             return null;
914         }
915     }
916
917     /**
918      * Calculates the first time an <CODE>NthIncludedDayTrigger</CODE> with
919      * <CODE>intervalType = {@link #INTERVAL_TYPE_YEARLY}</CODE> will fire
920      * after the specified date. See {@link #getNextFireTime} for more
921      * information.
922      *
923      * @param afterDate The time after which to find the nearest fire time.
924      * This argument is treated as exclusive &mdash; that is,
925      * if afterTime is a valid fire time for the trigger, it
926      * will not be returned as the next fire time.
927      * @return the first time the trigger will fire following the specified
928      * date
929      */

930     private Date JavaDoc getYearlyFireTimeAfter(Date JavaDoc afterDate) {
931         int currN = 0;
932         java.util.Calendar JavaDoc afterCal;
933         java.util.Calendar JavaDoc currCal;
934         int currYear;
935         int yearCount = 0;
936         boolean gotOne = false;
937         
938         afterCal = java.util.Calendar.getInstance(getTimeZone());
939         afterCal.setTime(afterDate);
940         
941         currCal = java.util.Calendar.getInstance(getTimeZone());
942         currCal.set(afterCal.get(java.util.Calendar.YEAR),
943                     java.util.Calendar.JANUARY, 1);
944         currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
945         currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
946         currCal.set(java.util.Calendar.SECOND, this.fireAtSecond);
947         currCal.set(java.util.Calendar.MILLISECOND, 0);
948         
949         currYear = currCal.get(java.util.Calendar.YEAR);
950         
951         while ((!gotOne) && (yearCount < this.nextFireCutoffInterval)) {
952             while ((currN != this.n) && (yearCount < 5)) {
953                 //if we move into a new year, reset the current "n" counter
954
if (currCal.get(java.util.Calendar.YEAR) != currYear) {
955                     currN = 0;
956                     yearCount++;
957                     currYear = currCal.get(java.util.Calendar.YEAR);
958                 }
959                 
960                 //treating a null calendar as an all-inclusive calendar,
961
// increment currN if the current date being tested is included
962
// on the calendar
963
if ((calendar == null)
964                         || (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
965                     currN++;
966                 }
967
968                 if (currN != this.n) {
969                     currCal.add(java.util.Calendar.DATE, 1);
970                 }
971                 
972                 //if we pass endTime, drop out and return null.
973
if ((this.endTime != null)
974                         && (currCal.getTime().after(this.endTime))) {
975                     return null;
976                 }
977             }
978             
979             //We found an "n" or we've checked the requisite number of years.
980
// If we've found an "n", is it the right one? -- that is, we
981
// could be looking at an nth day PRIOR to afterDate
982
if (currN == this.n) {
983                 if (afterDate.before(currCal.getTime())) {
984                     gotOne = true;
985                 } else { //resume checking on the first day of the next year
986
currCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
987                     currCal.set(java.util.Calendar.MONTH,
988                                 java.util.Calendar.JANUARY);
989                     currCal.add(java.util.Calendar.YEAR, 1);
990                     currN = 0;
991                 }
992             }
993         }
994         
995         if (yearCount < this.nextFireCutoffInterval) {
996             return currCal.getTime();
997         } else {
998             return null;
999         }
1000    }
1001}
Popular Tags