org.quartz.NthIncludedDayTrigger Maven / Gradle / Ivy
/*
* Copyright 2004-2005 OpenSymphony
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz;
import java.util.Date;
import org.quartz.Calendar;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Trigger;
/**
* A trigger which fires on the Nth day of every interval type
* ({@link #INTERVAL_TYPE_WEEKLY}, {@link #INTERVAL_TYPE_MONTHLY} or
* {@link #INTERVAL_TYPE_YEARLY}) that is not excluded by the associated
* calendar. When determining what the Nth day of the month or year
* is, NthIncludedDayTrigger
will skip excluded days on the
* associated calendar. This would commonly be used in an Nth
* business day situation, in which the user wishes to fire a particular job on
* the Nth business day (i.e. the 5th business day of
* every month). Each NthIncludedDayTrigger
also has an associated
* fireAtTime
which indicates at what time of day the trigger is
* to fire.
*
* All NthIncludedDayTrigger
s default to a monthly interval type
* (fires on the Nth day of every month) with N = 1 (first
* non-excluded day) and fireAtTime
set to 12:00 PM (noon). These
* values can be changed using the {@link #setN}, {@link #setIntervalType}, and
* {@link #setFireAtTime} methods. Users may also want to note the
* {@link #setNextFireCutoffInterval} and {@link #getNextFireCutoffInterval}
* methods.
*
* Take, for example, the following calendar:
*
*
* July August September
* Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
* 1 W 1 2 3 4 5 W 1 2 W
* W H 5 6 7 8 W W 8 9 10 11 12 W W H 6 7 8 9 W
* W 11 12 13 14 15 W W 15 16 17 18 19 W W 12 13 14 15 16 W
* W 18 19 20 21 22 W W 22 23 24 25 26 W W 19 20 21 22 23 W
* W 25 26 27 28 29 W W 29 30 31 W 26 27 28 29 30
* W
*
*
* Where W's represent weekend days, and H's represent holidays, all of which
* are excluded on a calendar associated with an
* NthIncludedDayTrigger
with n=5
and
* intervalType=INTERVAL_TYPE_MONTHLY
. In this case, the trigger
* would fire on the 8th of July (because of the July 4 holiday),
* the 5th of August, and the 8th of September (because
* of Labor Day).
*
* @author Aaron Craven
*/
public class NthIncludedDayTrigger extends Trigger {
/**
* Instructs the Scheduler
that upon a mis-fire situation, the
* NthIncludedDayTrigger
wants to be fired now by the
* Scheduler
*/
public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
/**
* Instructs the Scheduler
that upon a mis-fire situation, the
* NthIncludedDayTrigger
wants to have
* nextFireTime
updated to the next time in the schedule after
* the current time, but it does not want to be fired now.
*/
public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
/**
* indicates a monthly trigger type (fires on the Nth included
* day of every month).
*/
public static final int INTERVAL_TYPE_MONTHLY = 1;
/**
* indicates a yearly trigger type (fires on the Nth included
* day of every year).
*/
public static final int INTERVAL_TYPE_YEARLY = 2;
/**
* indicates a weekly trigger type (fires on the Nth included
* day of every week). When using this interval type, care must be taken
* not to think of the value of n
as an analog to
* java.util.Calendar.DAY_OF_WEEK
. Such a comparison can only
* be drawn when there are no calendars associated with the trigger. To
* illustrate, consider an NthIncludedDayTrigger
with
* n = 3
which is associated with a Calendar excluding
* non-weekdays. The trigger would fire on the 3rd
* included day of the week, which would be 4th
* actual day of the week.
*/
public static final int INTERVAL_TYPE_WEEKLY = 3;
private Date startTime = new Date();
private Date endTime;
private Date previousFireTime;
private Date nextFireTime;
private Calendar calendar;
private int n = 1;
private int intervalType = INTERVAL_TYPE_MONTHLY;
private int fireAtHour = 12;
private int fireAtMinute = 0;
private int nextFireCutoffInterval = 12;
/**
* Create an NthIncludedDayTrigger
with no specified name,
* group, or JobDetail
. This will result initially in a
* default monthly trigger that fires on the first day of every month at
* 12:00 PM (n
=1,
* intervalType={@link #INTERVAL_TYPE_MONTHLY}
,
* fireAtTime="12:00"
).
*
* Note that setName()
, setGroup()
,
* setJobName()
, and setJobGroup()
, must be
* called before the NthIncludedDayTrigger
can be placed into
* a Scheduler
.
*/
public NthIncludedDayTrigger() {
super();
}
/**
* Create an NthIncludedDayTrigger
with the given name and
* group but no specified JobDetail
. This will result
* initially in a default monthly trigger that fires on the first day of
* every month at 12:00 PM (n
=1,
* intervalType={@link #INTERVAL_TYPE_MONTHLY}
,
* fireAtTime="12:00"
).
*
* Note that setJobName()
and setJobGroup()
must
* be called before the NthIncludedDayTrigger
can be placed
* into a Scheduler
.
*
* @param name the name for the NthIncludedDayTrigger
* @param group the group for the NthIncludedDayTrigger
*/
public NthIncludedDayTrigger(String name, String group) {
super(name, group);
}
/**
* Create an NthIncludedDayTrigger
with the given name and
* group and the specified JobDetail
. This will result
* initially in a default monthly trigger that fires on the first day of
* every month at 12:00 PM (n
=1,
* intervalType={@link #INTERVAL_TYPE_MONTHLY}
,
* fireAtTime="12:00"
).
*
* @param name the name for the NthIncludedDayTrigger
* @param group the group for the NthIncludedDayTrigger
* @param jobName the name of the job to associate with the
* NthIncludedDayTrigger
* @param jobGroup the group containing the job to associate with the
* NthIncludedDayTrigger
*/
public NthIncludedDayTrigger(String name, String group, String jobName,
String jobGroup) {
super(name, group, jobName, jobGroup);
}
/**
* Sets the day of the interval on which the
* NthIncludedDayTrigger
should fire. If the Nth
* day of the interval does not exist (i.e. the 32nd of a
* month), the trigger simply will never fire. N may not be less than 1.
*
* @param n the day of the interval on which the trigger should fire.
* @throws java.lang.IllegalArgumentException
* the value entered for N was not valid (probably less than or
* equal to zero).
* @see #getN()
*/
public void setN(int n) {
if (n > 0) {
this.n = n;
} else {
throw new IllegalArgumentException("N must be greater than 0.");
}
}
/**
* Returns the day of the interval on which the
* NthIncludedDayTrigger
should fire.
*
* @return the value of n
* @see #setN(int)
*/
public int getN() {
return this.n;
}
/**
* Sets the interval type for the NthIncludedDayTrigger
. If
* {@link #INTERVAL_TYPE_MONTHLY}, the trigger will fire on the
* Nth included day of every month. If
* {@link #INTERVAL_TYPE_YEARLY}, the trigger will fire on the
* Nth included day of every year. If
* {@link #INTERVAL_TYPE_WEEKLY}, the trigger will fire on the
* Nth included day of every week.
*
* @param intervalType the interval type for the trigger
* @throws java.lang.IllegalArgumentException
* the value of intervalType
is not valid. Valid
* values are represented by the INTERVAL_TYPE_WEEKLY,
* INTERVAL_TYPE_MONTHLY and INTERVAL_TYPE_YEARLY constants.
* @see #getIntervalType()
* @see #INTERVAL_TYPE_WEEKLY
* @see #INTERVAL_TYPE_MONTHLY
* @see #INTERVAL_TYPE_YEARLY
*/
public void setIntervalType(int intervalType) {
switch (intervalType) {
case INTERVAL_TYPE_WEEKLY:
this.intervalType = intervalType;
break;
case INTERVAL_TYPE_MONTHLY:
this.intervalType = intervalType;
break;
case INTERVAL_TYPE_YEARLY:
this.intervalType = intervalType;
break;
default:
throw new IllegalArgumentException("Invalid Interval Type:"
+ intervalType);
}
}
/**
* Returns the interval type for the NthIncludedDayTrigger
.
*
* @return the trigger's interval type
* @see #setIntervalType(int)
* @see #INTERVAL_TYPE_WEEKLY
* @see #INTERVAL_TYPE_MONTHLY
* @see #INTERVAL_TYPE_YEARLY
*/
public int getIntervalType() {
return this.intervalType;
}
/**
* Sets the fire time for the NthIncludedDayTrigger
, which
* should be represented as a string with the format "HH:MM",
* with HH representing the 24-hour clock hour of the fire time. Hours can
* be represented as either a one-digit or two-digit number.
*
* @param fireAtTime the time at which the trigger should fire
* @throws java.lang.IllegalArgumentException
* the specified value for fireAtTime
could not be
* successfully parsed into a valid time of day.
* @see #getFireAtTime()
*/
public void setFireAtTime(String fireAtTime) {
int fireHour = 12;
int fireMinute = 0;
String[] components;
try {
int i = fireAtTime.indexOf(":");
fireHour = Integer.parseInt(fireAtTime.substring(0, i));
fireMinute = Integer.parseInt(fireAtTime.substring(i+1));
} catch (Exception e) {
fireHour = 12;
fireMinute = 0;
throw new
IllegalArgumentException("Could not parse time expression: "
+ e.getMessage());
} finally {
this.fireAtHour = fireHour;
this.fireAtMinute = fireMinute;
}
}
/**
* Returns the fire time for the NthIncludedDayTrigger
as a
* string with the format "HH:MM", with HH representing the
* 24-hour clock hour of the fire time.
*
* @return the fire time for the trigger
* @see #setFireAtTime(String)
*/
public String getFireAtTime() {
return this.fireAtHour + ":" + this.fireAtMinute;
}
/**
* Sets the nextFireCutoffInterval
for the
* NthIncludedDayTrigger
.
*
* Because of the conceptual design of NthIncludedDayTrigger
,
* it is not always possible to decide with certainty that the trigger
* will never fire again. Therefore, it will search for the next
* fire time up to a given cutoff. These cutoffs can be changed by using the
* {@link #setNextFireCutoffInterval(int)} and
* {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
* of the intervals specified by {@link #getIntervalType()
* intervalType}
.
*
* In most cases, the default value of this setting (12) is sufficient (it
* is highly unlikely, for example, that you will need to look at more than
* 12 months of dates to ensure that your trigger will never fire again).
* However, this setting is included to allow for the rare exceptions where
* this might not be true.
*
* For example, if your trigger is associated with a calendar that excludes
* a great many dates in the next 12 months, and hardly any following that,
* it is possible (if n
is large enough) that you could run
* into this situation.
*
* @param nextFireCutoffInterval the desired cutoff interval
* @see #getNextFireCutoffInterval()
* @see #getNextFireTime()
*/
public void setNextFireCutoffInterval(int nextFireCutoffInterval) {
this.nextFireCutoffInterval = nextFireCutoffInterval;
}
/**
* Returns the nextFireCutoffInterval
for the
* NthIncludedDayTrigger
.
*
* Because of the conceptual design of NthIncludedDayTrigger
,
* it is not always possible to decide with certainty that the trigger
* will never fire again. Therefore, it will search for the next
* fire time up to a given cutoff. These cutoffs can be changed by using the
* {@link #setNextFireCutoffInterval(int)} and
* {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
* of the intervals specified by {@link #getIntervalType()
* intervalType}
.
*
* @return the chosen cutoff interval
* @see #setNextFireCutoffInterval(int)
* @see #getNextFireTime()
*/
public int getNextFireCutoffInterval() {
return this.nextFireCutoffInterval;
}
/**
* Sets the date/time on which the trigger may begin firing. This defines
* the initial boundary for trigger firings — the trigger will not
* fire prior to this date and time. Defaults to the current date and time
* when the NthIncludedDayTrigger
is created.
*
* @param startTime the initial boundary for trigger firings
* @throws java.lang.IllegalArgumentException
* the specified start time is after the current end time or is
* null
* @see #getStartTime()
*/
public void setStartTime(Date startTime) {
if (startTime == null) {
throw new IllegalArgumentException("Start time may not be null");
}
if ((this.endTime != null) && endTime.before(startTime)) {
throw new IllegalArgumentException("Start time must be before end time.");
}
this.startTime = startTime;
}
/**
* Returns the date/time on which the trigger may begin firing. This
* defines the initial boundary for trigger firings — the trigger
* will not fire prior to this date and time.
*
* @return the initial date/time on which the trigger may begin firing
* @see #setStartTime(Date)
*/
public Date getStartTime() {
return this.startTime;
}
/**
* Sets the date/time on which the trigger must stop firing. This defines
* the final boundary for trigger firings — the trigger will not
* fire after to this date and time. If this value is null, no end time
* boundary is assumed, and the trigger can continue indefinitely.
*
* @param endTime the final boundary for trigger firings
* @throws java.lang.IllegalArgumentException
* the specified end time is before the current start time
* @see #getEndTime()
*/
public void setEndTime(Date endTime) {
if ((endTime != null) && endTime.before(startTime)) {
throw new IllegalArgumentException("End time must be after start time.");
}
this.endTime = endTime;
}
/**
* Returns the date/time on which the trigger must stop firing. This
* defines the final boundary for trigger firings — the trigger will
* not fire after to this date and time. If this value is null, no end time
* boundary is assumed, and the trigger can continue indefinitely.
*
* @return the date/time on which the trigger must stop firing
* @see #setEndTime(Date)
*/
public Date getEndTime() {
return this.endTime;
}
/**
* Returns the next time at which the NthIncludedDayTrigger
* will fire. If the trigger will not fire again, null
will be
* returned.
*
* Because of the conceptual design of NthIncludedDayTrigger
,
* it is not always possible to decide with certainty that the trigger
* will never fire again. Therefore, it will search for the next
* fire time up to a given cutoff. These cutoffs can be changed by using the
* {@link #setNextFireCutoffInterval(int)} and
* {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
* of the intervals specified by {@link #getIntervalType()
* intervalType}
.
*
* The returned value is not guaranteed to be valid until after
* the trigger has been added to the scheduler.
*
* @return the next fire time for the trigger
* @see #getNextFireCutoffInterval()
* @see #setNextFireCutoffInterval(int)
* @see #getFireTimeAfter(Date)
*/
public Date getNextFireTime() {
return this.nextFireTime;
}
/**
* Returns the previous time at which the
* NthIncludedDayTrigger
fired. If the trigger has not yet
* fired, null
will be returned.
*
* @return the previous fire time for the trigger
*/
public Date getPreviousFireTime() {
return this.previousFireTime;
}
/**
* Returns the first time the NthIncludedDayTrigger
will fire
* after the specified date.
*
* Because of the conceptual design of NthIncludedDayTrigger
,
* it is not always possible to decide with certainty that the trigger
* will never fire again. Therefore, it will search for the next
* fire time up to a given cutoff. These cutoffs can be changed by using the
* {@link #setNextFireCutoffInterval(int)} and
* {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
* of the intervals specified by {@link #getIntervalType()
* intervalType}
.
*
* Therefore, for triggers with intervalType =
* {@link NthIncludedDayTrigger#INTERVAL_TYPE_WEEKLY
* INTERVAL_TYPE_WEEKLY}
, if the trigger will not fire within 12
* weeks after the given date/time, null
will be returned. For
* triggers with intervalType =
* {@link NthIncludedDayTrigger#INTERVAL_TYPE_MONTHLY
* INTERVAL_TYPE_MONTHLY}
, if the trigger will not fire within 12
* months after the given date/time, null
will be returned.
* For triggers with intervalType =
* {@link NthIncludedDayTrigger#INTERVAL_TYPE_YEARLY
* INTERVAL_TYPE_YEARLY}
, if the trigger will not fire within 12
* years after the given date/time, null
will be returned. In
* all cases, if the trigger will not fire before endTime
,
* null
will be returned.
*
* @param afterTime The time after which to find the nearest fire time.
* This argument is treated as exclusive — that is,
* if afterTime is a valid fire time for the trigger, it
* will not be returned as the next fire time.
* @return the first time the trigger will fire following the specified
* date
*/
public Date getFireTimeAfter(Date afterTime) {
if (afterTime == null) {
afterTime = new Date();
}
if (afterTime.before(this.startTime)) {
afterTime = new Date(startTime.getTime() - 1000l);
}
if (this.intervalType == INTERVAL_TYPE_WEEKLY) {
return getWeeklyFireTimeAfter(afterTime);
} else if (this.intervalType == INTERVAL_TYPE_MONTHLY) {
return getMonthlyFireTimeAfter(afterTime);
} else if (this.intervalType == INTERVAL_TYPE_YEARLY) {
return getYearlyFireTimeAfter(afterTime);
} else {
return null;
}
}
/**
* Returns the last time the NthIncludedDayTrigger
will fire.
* If the trigger will not fire at any point between startTime
* and endTime
, null
will be returned.
*
* @return the last time the trigger will fire.
*/
public Date getFinalFireTime() {
Date finalTime = null;
java.util.Calendar currCal = java.util.Calendar.getInstance();
currCal.setTime(this.endTime);
while ((finalTime == null)
&& (this.startTime.before(currCal.getTime()))) {
currCal.add(java.util.Calendar.DATE, -1);
finalTime = getFireTimeAfter(currCal.getTime());
}
return finalTime;
}
/**
* Called when the Scheduler
has decided to 'fire' the trigger
* (execute the associated Job
), in order to give the
* Trigger
a chance to update itself for its next triggering
* (if any).
*/
public void triggered(Calendar calendar) {
this.calendar = calendar;
this.previousFireTime = this.nextFireTime;
this.nextFireTime = getFireTimeAfter(this.nextFireTime);
}
/**
* Called by the scheduler at the time a Trigger
is first
* added to the scheduler, in order to have the Trigger
* compute its first fire time, based on any associated calendar.
*
* After this method has been called, getNextFireTime()
* should return a valid answer.
*
*
* @return the first time at which the Trigger
will be fired
* by the scheduler, which is also the same value
* {@link #getNextFireTime()} will return (until after the first
* firing of the Trigger
).
*/
public Date computeFirstFireTime(Calendar calendar) {
this.calendar = calendar;
this.nextFireTime =
getFireTimeAfter(new Date(this.startTime.getTime() - 1000l));
return this.nextFireTime;
}
/**
* Called after the Scheduler
has executed the
* JobDetail
associated with the Trigger
in order
* to get the final instruction code from the trigger.
*
* @param jobCtx the JobExecutionContext
that was used by the
* Job
's execute()
method.
* @param result the JobExecutionException
thrown by the
* Job
, if any (may be null
)
* @return one of the Trigger.INSTRUCTION_XXX constants.
*/
public int executionComplete(JobExecutionContext jobCtx,
JobExecutionException result) {
if (result != null && result.refireImmediately())
return INSTRUCTION_RE_EXECUTE_JOB;
if (result != null && result.unscheduleFiringTrigger())
return INSTRUCTION_SET_TRIGGER_COMPLETE;
if (result != null && result.unscheduleAllTriggers())
return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
if (!mayFireAgain()) return INSTRUCTION_DELETE_TRIGGER;
return INSTRUCTION_NOOP;
}
/**
* Used by the Scheduler
to determine whether or not it is
* possible for this Trigger
to fire again.
*
* If the returned value is false
then the
* Scheduler
may remove the Trigger
from the
* JobStore
*
* @return a boolean indicator of whether the trigger could potentially fire
* again
*/
public boolean mayFireAgain() {
return (getNextFireTime() == null);
}
/**
* Indicates whether misfireInstruction
is a valid misfire
* instruction for this Trigger
.
*
* @return whether misfireInstruction
is valid.
*/
protected boolean validateMisfireInstruction(int misfireInstruction) {
if ((misfireInstruction == MISFIRE_INSTRUCTION_SMART_POLICY) ||
(misfireInstruction == MISFIRE_INSTRUCTION_DO_NOTHING) ||
(misfireInstruction == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW)) {
return true;
} else {
return false;
}
}
/**
* Updates the NthIncludedDayTrigger
's state based on the
* MISFIRE_INSTRUCTION_XXX that was selected when the
* NthIncludedDayTrigger
was created
*
* If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
* then the instruction will be interpreted as
* {@link #MISFIRE_INSTRUCTION_FIRE_ONCE_NOW}.
*
* @param calendar a new or updated calendar to use for the trigger
*/
public void updateAfterMisfire(Calendar calendar) {
int instruction = getMisfireInstruction();
this.calendar = calendar;
if (instruction == MISFIRE_INSTRUCTION_SMART_POLICY) {
instruction = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
}
if (instruction == MISFIRE_INSTRUCTION_DO_NOTHING) {
this.nextFireTime = getFireTimeAfter(new Date());
} else if (instruction == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
this.nextFireTime = new Date();
}
}
/**
* Updates the NthIncludedDayTrigger
's state based on the
* given new version of the associated Calendar
.
*
* @param calendar a new or updated calendar to use for the trigger
* @param misfireThreshold the amount of time (in milliseconds) that must
* be between "now" and the time the next
* firing of the trigger is supposed to occur.
*/
public void updateWithNewCalendar(Calendar calendar,
long misfireThreshold) {
Date now = new Date();
long diff;
this.calendar = calendar;
this.nextFireTime = getFireTimeAfter(this.previousFireTime);
if ((this.nextFireTime != null) && (this.nextFireTime.before(now))) {
diff = now.getTime() - this.nextFireTime.getTime();
if (diff >= misfireThreshold) {
this.nextFireTime = getFireTimeAfter(this.nextFireTime);
}
}
}
/**
* Calculates the first time an NthIncludedDayTrigger
with
* intervalType = {@link #INTERVAL_TYPE_WEEKLY}
will fire
* after the specified date. See {@link #getNextFireTime} for more
* information.
*
* @param afterDate The time after which to find the nearest fire time.
* This argument is treated as exclusive — that is,
* if afterTime is a valid fire time for the trigger, it
* will not be returned as the next fire time.
* @return the first time the trigger will fire following the specified
* date
*/
private Date getWeeklyFireTimeAfter(Date afterDate) {
int currN = 0;
java.util.Calendar afterCal;
java.util.Calendar currCal;
int currWeek;
int weekCount = 0;
boolean gotOne = false;
afterCal = java.util.Calendar.getInstance();
afterCal.setTime(afterDate);
currCal = java.util.Calendar.getInstance();
currCal.set(afterCal.get(java.util.Calendar.YEAR),
afterCal.get(java.util.Calendar.MONTH),
afterCal.get(java.util.Calendar.DAY_OF_MONTH));
//move to the first day of the week (SUNDAY)
currCal.add(java.util.Calendar.DAY_OF_MONTH,
(afterCal.get(java.util.Calendar.DAY_OF_WEEK) - 1) * -1);
currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
currCal.set(java.util.Calendar.SECOND, 0);
currCal.set(java.util.Calendar.MILLISECOND, 0);
currWeek = currCal.get(java.util.Calendar.WEEK_OF_YEAR);
while ((!gotOne) && (weekCount < this.nextFireCutoffInterval)) {
while ((currN != this.n) && (weekCount < 12)) {
//if we move into a new month, reset the current "n" counter
if (currCal.get(java.util.Calendar.WEEK_OF_YEAR) != currWeek) {
currN = 0;
weekCount++;
currWeek = currCal.get(java.util.Calendar.WEEK_OF_YEAR);
}
//treating a null calendar as an all-inclusive calendar,
// increment currN if the current date being tested is included
// on the calendar
if ((calendar == null)
|| (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
currN++;
}
if (currN != this.n) {
currCal.add(java.util.Calendar.DATE, 1);
}
//if we pass endTime, drop out and return null.
if ((this.endTime != null)
&& (currCal.getTime().after(this.endTime))) {
return null;
}
}
//We found an "n" or we've checked the requisite number of weeks.
// If we've found an "n", is it the right one? -- that is, we could
// be looking at an nth day PRIOR to afterDate
if (currN == this.n) {
if (afterDate.before(currCal.getTime())) {
gotOne = true;
} else { //resume checking on the first day of the next week
currCal.add(java.util.Calendar.DAY_OF_MONTH, -1 * (currN - 1));
currCal.add(java.util.Calendar.DAY_OF_MONTH, 7);
currN = 0;
}
}
}
if (weekCount < this.nextFireCutoffInterval) {
return currCal.getTime();
} else {
return null;
}
}
/**
* Calculates the first time an NthIncludedDayTrigger
with
* intervalType = {@link #INTERVAL_TYPE_MONTHLY}
will fire
* after the specified date. See {@link #getNextFireTime} for more
* information.
*
* @param afterDate The time after which to find the nearest fire time.
* This argument is treated as exclusive — that is,
* if afterTime is a valid fire time for the trigger, it
* will not be returned as the next fire time.
* @return the first time the trigger will fire following the specified
* date
*/
private Date getMonthlyFireTimeAfter(Date afterDate) {
int currN = 0;
java.util.Calendar afterCal;
java.util.Calendar currCal;
int currMonth;
int monthCount = 0;
boolean gotOne = false;
afterCal = java.util.Calendar.getInstance();
afterCal.setTime(afterDate);
currCal = java.util.Calendar.getInstance();
currCal.set(afterCal.get(java.util.Calendar.YEAR),
afterCal.get(java.util.Calendar.MONTH), 1);
currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
currCal.set(java.util.Calendar.SECOND, 0);
currCal.set(java.util.Calendar.MILLISECOND, 0);
currMonth = currCal.get(java.util.Calendar.MONTH);
while ((!gotOne) && (monthCount < this.nextFireCutoffInterval)) {
while ((currN != this.n) && (monthCount < 12)) {
//if we move into a new month, reset the current "n" counter
if (currCal.get(java.util.Calendar.MONTH) != currMonth) {
currN = 0;
monthCount++;
currMonth = currCal.get(java.util.Calendar.MONTH);
}
//treating a null calendar as an all-inclusive calendar,
// increment currN if the current date being tested is included
// on the calendar
if ((calendar == null)
|| (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
currN++;
}
if (currN != this.n) {
currCal.add(java.util.Calendar.DATE, 1);
}
//if we pass endTime, drop out and return null.
if ((this.endTime != null)
&& (currCal.getTime().after(this.endTime))) {
return null;
}
}
//We found an "n" or we've checked the requisite number of months.
// If we've found an "n", is it the right one? -- that is, we could
// be looking at an nth day PRIOR to afterDate
if (currN == this.n) {
if (afterDate.before(currCal.getTime())) {
gotOne = true;
} else { //resume checking on the first day of the next month
currCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
currCal.add(java.util.Calendar.MONTH, 1);
currN = 0;
}
}
}
if (monthCount < this.nextFireCutoffInterval) {
return currCal.getTime();
} else {
return null;
}
}
/**
* Calculates the first time an NthIncludedDayTrigger
with
* intervalType = {@link #INTERVAL_TYPE_YEARLY}
will fire
* after the specified date. See {@link #getNextFireTime} for more
* information.
*
* @param afterDate The time after which to find the nearest fire time.
* This argument is treated as exclusive — that is,
* if afterTime is a valid fire time for the trigger, it
* will not be returned as the next fire time.
* @return the first time the trigger will fire following the specified
* date
*/
private Date getYearlyFireTimeAfter(Date afterDate) {
int currN = 0;
java.util.Calendar afterCal;
java.util.Calendar currCal;
int currYear;
int yearCount = 0;
boolean gotOne = false;
afterCal = java.util.Calendar.getInstance();
afterCal.setTime(afterDate);
currCal = java.util.Calendar.getInstance();
currCal.set(afterCal.get(java.util.Calendar.YEAR),
java.util.Calendar.JANUARY, 1);
currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
currCal.set(java.util.Calendar.SECOND, 0);
currCal.set(java.util.Calendar.MILLISECOND, 0);
currYear = currCal.get(java.util.Calendar.YEAR);
while ((!gotOne) && (yearCount < this.nextFireCutoffInterval)) {
while ((currN != this.n) && (yearCount < 5)) {
//if we move into a new year, reset the current "n" counter
if (currCal.get(java.util.Calendar.YEAR) != currYear) {
currN = 0;
yearCount++;
currYear = currCal.get(java.util.Calendar.YEAR);
}
//treating a null calendar as an all-inclusive calendar,
// increment currN if the current date being tested is included
// on the calendar
if ((calendar == null)
|| (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
currN++;
}
if (currN != this.n) {
currCal.add(java.util.Calendar.DATE, 1);
}
//if we pass endTime, drop out and return null.
if ((this.endTime != null)
&& (currCal.getTime().after(this.endTime))) {
return null;
}
}
//We found an "n" or we've checked the requisite number of years.
// If we've found an "n", is it the right one? -- that is, we
// could be looking at an nth day PRIOR to afterDate
if (currN == this.n) {
if (afterDate.before(currCal.getTime())) {
gotOne = true;
} else { //resume checking on the first day of the next year
currCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
currCal.set(java.util.Calendar.MONTH,
java.util.Calendar.JANUARY);
currCal.add(java.util.Calendar.YEAR, 1);
currN = 0;
}
}
}
if (yearCount < this.nextFireCutoffInterval) {
return currCal.getTime();
} else {
return null;
}
}
}