io.logspace.agent.shaded.quartz.impl.triggers.DailyTimeIntervalTriggerImpl Maven / Gradle / Ivy
/*
* Copyright 2001-2009 Terracotta, Inc.
*
* 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 io.logspace.agent.shaded.quartz.impl.triggers;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import io.logspace.agent.shaded.quartz.DailyTimeIntervalScheduleBuilder;
import io.logspace.agent.shaded.quartz.DailyTimeIntervalTrigger;
import io.logspace.agent.shaded.quartz.DateBuilder;
import io.logspace.agent.shaded.quartz.JobExecutionContext;
import io.logspace.agent.shaded.quartz.JobExecutionException;
import io.logspace.agent.shaded.quartz.ScheduleBuilder;
import io.logspace.agent.shaded.quartz.Scheduler;
import io.logspace.agent.shaded.quartz.SchedulerException;
import io.logspace.agent.shaded.quartz.TimeOfDay;
import io.logspace.agent.shaded.quartz.Trigger;
import io.logspace.agent.shaded.quartz.DateBuilder.IntervalUnit;
/**
* A concrete implementation of DailyTimeIntervalTrigger that is used to fire a {@link io.logspace.agent.shaded.quartz.JobDetail}
* based upon daily repeating time intervals.
*
* The trigger will fire every N (see {@link #setRepeatInterval(int)} ) seconds, minutes or hours
* (see {@link #setRepeatIntervalUnit(io.logspace.agent.shaded.quartz.DateBuilder.IntervalUnit)}) during a given time window on specified days of the week.
*
* For example#1, a trigger can be set to fire every 72 minutes between 8:00 and 11:00 everyday. It's fire times would
* be 8:00, 9:12, 10:24, then next day would repeat: 8:00, 9:12, 10:24 again.
*
* For example#2, a trigger can be set to fire every 23 minutes between 9:20 and 16:47 Monday through Friday.
*
* On each day, the starting fire time is reset to startTimeOfDay value, and then it will add repeatInterval value to it until
* the endTimeOfDay is reached. If you set daysOfWeek values, then fire time will only occur during those week days period. Again,
* remember this trigger will reset fire time each day with startTimeOfDay, regardless of your interval or endTimeOfDay!
*
* The default values for fields if not set are: startTimeOfDay defaults to 00:00:00, the endTimeOfDay default to 23:59:59,
* and daysOfWeek is default to every day. The startTime default to current time-stamp now, while endTime has not value.
*
* If startTime is before startTimeOfDay, then startTimeOfDay will be used and startTime has no affect other than to specify
* the first day of firing. Else if startTime is
* after startTimeOfDay, then the first fire time for that day will be the next interval after the startTime. For example, if
* you set startingTimeOfDay=9am, endingTimeOfDay=11am, interval=15 mins, and startTime=9:33am, then the next fire time will
* be 9:45pm. Note also that if you do not set startTime value, the trigger builder will default to current time, and current time
* maybe before or after the startTimeOfDay! So be aware how you set your startTime.
*
* This trigger also supports "repeatCount" feature to end the trigger fire time after
* a certain number of count is reached. Just as the SimpleTrigger, setting repeatCount=0
* means trigger will fire once only! Setting any positive count then the trigger will repeat
* count + 1 times. Unlike SimpleTrigger, the default value of repeatCount of this trigger
* is set to REPEAT_INDEFINITELY instead of 0 though.
*
* @see DailyTimeIntervalTrigger
* @see DailyTimeIntervalScheduleBuilder
*
* @since 2.1.0
*
* @author James House
* @author Zemian Deng
*/
public class DailyTimeIntervalTriggerImpl extends AbstractTrigger implements DailyTimeIntervalTrigger, CoreTrigger {
private static final long serialVersionUID = -632667786771388749L;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constants.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private Date startTime = null;
private Date endTime = null;
private Date nextFireTime = null;
private Date previousFireTime = null;
private int repeatCount = REPEAT_INDEFINITELY;
private int repeatInterval = 1;
private IntervalUnit repeatIntervalUnit = IntervalUnit.MINUTE;
private Set daysOfWeek;
private TimeOfDay startTimeOfDay;
private TimeOfDay endTimeOfDay;
private int timesTriggered = 0;
private boolean complete = false;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Create a DailyTimeIntervalTrigger
with no settings.
*
*/
public DailyTimeIntervalTriggerImpl() {
super();
}
/**
*
* Create a DailyTimeIntervalTrigger
that will occur immediately, and
* repeat at the the given interval.
*
*
* @param startTimeOfDay
* The TimeOfDay
that the repeating should begin occurring.
* @param endTimeOfDay
* The TimeOfDay
that the repeating should stop occurring.
* @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are
* {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
* @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less.
*/
public DailyTimeIntervalTriggerImpl(String name, TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay, IntervalUnit intervalUnit, int repeatInterval) {
this(name, null, startTimeOfDay, endTimeOfDay, intervalUnit, repeatInterval);
}
/**
*
* Create a DailyTimeIntervalTrigger
that will occur immediately, and
* repeat at the the given interval.
*
*
* @param startTimeOfDay
* The TimeOfDay
that the repeating should begin occurring.
* @param endTimeOfDay
* The TimeOfDay
that the repeating should stop occurring.
* @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are
* {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
* @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less.
*/
public DailyTimeIntervalTriggerImpl(String name, String group, TimeOfDay startTimeOfDay,
TimeOfDay endTimeOfDay, IntervalUnit intervalUnit, int repeatInterval) {
this(name, group, new Date(), null, startTimeOfDay, endTimeOfDay, intervalUnit, repeatInterval);
}
/**
*
* Create a DailyTimeIntervalTrigger
that will occur at the given time,
* and repeat at the the given interval until the given end time.
*
*
* @param startTime
* A Date
set to the time for the Trigger
* to fire.
* @param endTime
* A Date
set to the time for the Trigger
* to quit repeat firing.
* @param startTimeOfDay
* The TimeOfDay
that the repeating should begin occurring.
* @param endTimeOfDay
* The TimeOfDay
that the repeating should stop occurring.
* @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are
* {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
* @param repeatInterval
* The number of milliseconds to pause between the repeat firing.
* @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less.
*/
public DailyTimeIntervalTriggerImpl(String name, Date startTime,
Date endTime, TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay,
IntervalUnit intervalUnit, int repeatInterval) {
this(name, null, startTime, endTime, startTimeOfDay, endTimeOfDay, intervalUnit, repeatInterval);
}
/**
*
* Create a DailyTimeIntervalTrigger
that will occur at the given time,
* and repeat at the the given interval until the given end time.
*
*
* @param startTime
* A Date
set to the time for the Trigger
* to fire.
* @param endTime
* A Date
set to the time for the Trigger
* to quit repeat firing.
* @param startTimeOfDay
* The TimeOfDay
that the repeating should begin occurring.
* @param endTimeOfDay
* The TimeOfDay
that the repeating should stop occurring.
* @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are
* {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
* @param repeatInterval
* The number of milliseconds to pause between the repeat firing.
* @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less.
*/
public DailyTimeIntervalTriggerImpl(String name, String group, Date startTime,
Date endTime, TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay,
IntervalUnit intervalUnit, int repeatInterval) {
super(name, group);
setStartTime(startTime);
setEndTime(endTime);
setRepeatIntervalUnit(intervalUnit);
setRepeatInterval(repeatInterval);
setStartTimeOfDay(startTimeOfDay);
setEndTimeOfDay(endTimeOfDay);
}
/**
*
* Create a DailyTimeIntervalTrigger
that will occur at the given time,
* fire the identified Job
and repeat at the the given
* interval until the given end time.
*
*
* @param startTime
* A Date
set to the time for the Trigger
* to fire.
* @param endTime
* A Date
set to the time for the Trigger
* to quit repeat firing.
* @param startTimeOfDay
* The TimeOfDay
that the repeating should begin occurring.
* @param endTimeOfDay
* The TimeOfDay
that the repeating should stop occurring.
* @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are
* {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
* @param repeatInterval
* The number of milliseconds to pause between the repeat firing.
* @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less.
*/
public DailyTimeIntervalTriggerImpl(String name, String group, String jobName,
String jobGroup, Date startTime, Date endTime,
TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay,
IntervalUnit intervalUnit, int repeatInterval) {
super(name, group, jobName, jobGroup);
setStartTime(startTime);
setEndTime(endTime);
setRepeatIntervalUnit(intervalUnit);
setRepeatInterval(repeatInterval);
setStartTimeOfDay(startTimeOfDay);
setEndTimeOfDay(endTimeOfDay);
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Get the time at which the DailyTimeIntervalTrigger
should occur. It defaults to
* the getStartTimeOfDay of current day.
*
*/
@Override
public Date getStartTime() {
if(startTime == null) {
startTime = new Date();
}
return startTime;
}
/**
*
* Set the time at which the DailyTimeIntervalTrigger
should occur.
*
*
* @exception IllegalArgumentException
* if startTime is null
.
*/
@Override
public void setStartTime(Date startTime) {
if (startTime == null) {
throw new IllegalArgumentException("Start time cannot be null");
}
Date eTime = getEndTime();
if (eTime != null && eTime.before(startTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.startTime = startTime;
}
/**
*
* Get the time at which the DailyTimeIntervalTrigger
should quit
* repeating.
*
*
* @see #getFinalFireTime()
*/
@Override
public Date getEndTime() {
return endTime;
}
/**
*
* Set the time at which the DailyTimeIntervalTrigger
should quit
* repeating (and be automatically deleted).
*
*
* @exception IllegalArgumentException
* if endTime is before start time.
*/
@Override
public void setEndTime(Date endTime) {
Date sTime = getStartTime();
if (sTime != null && endTime != null && sTime.after(endTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.endTime = endTime;
}
/* (non-Javadoc)
* @see io.logspace.agent.shaded.quartz.DailyTimeIntervalTriggerI#getRepeatIntervalUnit()
*/
public IntervalUnit getRepeatIntervalUnit() {
return repeatIntervalUnit;
}
/**
* Set the interval unit - the time unit on with the interval applies.
*
* @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are
* {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
*/
public void setRepeatIntervalUnit(IntervalUnit intervalUnit) {
if (repeatIntervalUnit == null ||
!((repeatIntervalUnit.equals(IntervalUnit.SECOND) ||
repeatIntervalUnit.equals(IntervalUnit.MINUTE) ||
repeatIntervalUnit.equals(IntervalUnit.HOUR))))
throw new IllegalArgumentException("Invalid repeat IntervalUnit (must be SECOND, MINUTE or HOUR).");
this.repeatIntervalUnit = intervalUnit;
}
/* (non-Javadoc)
* @see io.logspace.agent.shaded.quartz.DailyTimeIntervalTriggerI#getRepeatInterval()
*/
public int getRepeatInterval() {
return repeatInterval;
}
/**
*
* set the the time interval that will be added to the DailyTimeIntervalTrigger
's
* fire time (in the set repeat interval unit) in order to calculate the time of the
* next trigger repeat.
*
*
* @exception IllegalArgumentException
* if repeatInterval is < 1
*/
public void setRepeatInterval( int repeatInterval) {
if (repeatInterval < 0) {
throw new IllegalArgumentException(
"Repeat interval must be >= 1");
}
this.repeatInterval = repeatInterval;
}
/* (non-Javadoc)
* @see io.logspace.agent.shaded.quartz.DailyTimeIntervalTriggerI#getTimesTriggered()
*/
public int getTimesTriggered() {
return timesTriggered;
}
/**
*
* Set the number of times the DailyTimeIntervalTrigger
has already
* fired.
*
*/
public void setTimesTriggered(int timesTriggered) {
this.timesTriggered = timesTriggered;
}
@Override
protected boolean validateMisfireInstruction(int misfireInstruction) {
return misfireInstruction >= MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY && misfireInstruction <= MISFIRE_INSTRUCTION_DO_NOTHING;
}
/**
*
* Updates the DailyTimeIntervalTrigger
's state based on the
* MISFIRE_INSTRUCTION_XXX that was selected when the DailyTimeIntervalTrigger
* was created.
*
*
*
* If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
* then the following scheme will be used:
*
* - The instruction will be interpreted as
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
*
*
*/
@Override
public void updateAfterMisfire(io.logspace.agent.shaded.quartz.Calendar cal) {
int instr = getMisfireInstruction();
if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)
return;
if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) {
instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
}
if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null
&& !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
// fire once now...
setNextFireTime(new Date());
// the new fire time afterward will magically preserve the original
// time of day for firing for day/week/month interval triggers,
// because of the way getFireTimeAfter() works - in its always restarting
// computation from the start time.
}
}
/**
*
* Called when the {@link 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).
*
*
* @see #executionComplete(JobExecutionContext, JobExecutionException)
*/
@Override
public void triggered(io.logspace.agent.shaded.quartz.Calendar calendar) {
timesTriggered++;
previousFireTime = nextFireTime;
nextFireTime = getFireTimeAfter(nextFireTime);
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
}
if (nextFireTime == null) {
complete = true;
}
}
/**
* @see io.logspace.agent.shaded.quartz.impl.triggers.AbstractTrigger#updateWithNewCalendar(io.logspace.agent.shaded.quartz.Calendar, long)
*/
@Override
public void updateWithNewCalendar(io.logspace.agent.shaded.quartz.Calendar calendar, long misfireThreshold)
{
nextFireTime = getFireTimeAfter(previousFireTime);
if (nextFireTime == null || calendar == null) {
return;
}
Date now = new Date();
while (nextFireTime != null && !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
if(nextFireTime != null && nextFireTime.before(now)) {
long diff = now.getTime() - nextFireTime.getTime();
if(diff >= misfireThreshold) {
nextFireTime = getFireTimeAfter(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 getNextFireTime()
* will return (until after the first firing of the Trigger
).
*
*/
@Override
public Date computeFirstFireTime(io.logspace.agent.shaded.quartz.Calendar calendar) {
Date sTime = getStartTime();
Date startTimeOfDayDate = getStartTimeOfDay().getTimeOfDayForDate(sTime);
if(DateBuilder.evenSecondDate(startTime).equals(startTimeOfDayDate)) {
return startTime;
}
else if (sTime.after(startTimeOfDayDate)) {
// If startTime is after the timeOfDay, then look for the next time
nextFireTime = getFireTimeAfter(sTime);
} else {
// If startTime is before the timeOfDay then advance to timeOfDay (and if necessary dayOfWeek)
nextFireTime = advanceToNextDayOfWeekIfNecessary(startTimeOfDayDate, false);
}
// Check calendar for date-time exclusion
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
return null;
}
}
return nextFireTime;
}
private Calendar createCalendarTime(Date dateTime) {
Calendar cal = Calendar.getInstance();
cal.setTime(dateTime);
return cal;
}
/**
*
* Returns the next time at which the Trigger
is scheduled to fire. If
* the trigger will not fire again, null
will be returned. Note that
* the time returned can possibly be in the past, if the time that was computed
* for the trigger to next fire has already arrived, but the scheduler has not yet
* been able to fire the trigger (which would likely be due to lack of resources
* e.g. threads).
*
*
* The value returned is not guaranteed to be valid until after the Trigger
* has been added to the scheduler.
*
*/
@Override
public Date getNextFireTime() {
return nextFireTime;
}
/**
*
* Returns the previous time at which the DailyTimeIntervalTrigger
* fired. If the trigger has not yet fired, null
will be
* returned.
*/
@Override
public Date getPreviousFireTime() {
return previousFireTime;
}
/**
*
* Set the next time at which the DailyTimeIntervalTrigger
should fire.
*
*
*
* This method should not be invoked by client code.
*
*/
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
/**
*
* Set the previous time at which the DailyTimeIntervalTrigger
fired.
*
*
*
* This method should not be invoked by client code.
*
*/
public void setPreviousFireTime(Date previousFireTime) {
this.previousFireTime = previousFireTime;
}
/**
*
* Returns the next time at which the DailyTimeIntervalTrigger
will
* fire, after the given time. If the trigger will not fire after the given
* time, null
will be returned.
*
*/
@Override
public Date getFireTimeAfter(Date afterTime) {
// Check if trigger has completed or not.
if (complete) {
return null;
}
// Check repeatCount limit
if (repeatCount != REPEAT_INDEFINITELY && timesTriggered > repeatCount) {
return null;
}
// a. Increment afterTime by a second, so that we are comparing against a time after it!
if (afterTime == null) {
afterTime = new Date(System.currentTimeMillis() + 1000L);
} else {
afterTime = new Date(afterTime.getTime() + 1000L);
}
// make sure afterTime is at least startTime
if(afterTime.before(startTime))
afterTime = startTime;
// b.Check to see if afterTime is after endTimeOfDay or not. If yes, then we need to advance to next day as well.
boolean afterTimePastEndTimeOfDay = false;
if (endTimeOfDay != null) {
afterTimePastEndTimeOfDay = afterTime.getTime() > endTimeOfDay.getTimeOfDayForDate(afterTime).getTime();
}
// c. now we need to move move to the next valid day of week if either:
// the given time is past the end time of day, or given time is not on a valid day of week
Date fireTime = advanceToNextDayOfWeekIfNecessary(afterTime, afterTimePastEndTimeOfDay);
if (fireTime == null)
return null;
// d. Calculate and save fireTimeEndDate variable for later use
Date fireTimeEndDate = null;
if (endTimeOfDay == null)
fireTimeEndDate = new TimeOfDay(23, 59, 59).getTimeOfDayForDate(fireTime);
else
fireTimeEndDate = endTimeOfDay.getTimeOfDayForDate(fireTime);
// e. Check fireTime against startTime or startTimeOfDay to see which go first.
Date fireTimeStartDate = startTimeOfDay.getTimeOfDayForDate(fireTime);
if (fireTime.before(fireTimeStartDate)) {
return fireTimeStartDate;
}
// f. Continue to calculate the fireTime by incremental unit of intervals.
// recall that if fireTime was less that fireTimeStartDate, we didn't get this far
long fireMillis = fireTime.getTime();
long startMillis = fireTimeStartDate.getTime();
long secondsAfterStart = (fireMillis - startMillis) / 1000L;
long repeatLong = getRepeatInterval();
Calendar sTime = createCalendarTime(fireTimeStartDate);
IntervalUnit repeatUnit = getRepeatIntervalUnit();
if(repeatUnit.equals(IntervalUnit.SECOND)) {
long jumpCount = secondsAfterStart / repeatLong;
if(secondsAfterStart % repeatLong != 0)
jumpCount++;
sTime.add(Calendar.SECOND, getRepeatInterval() * (int)jumpCount);
fireTime = sTime.getTime();
} else if(repeatUnit.equals(IntervalUnit.MINUTE)) {
long jumpCount = secondsAfterStart / (repeatLong * 60L);
if(secondsAfterStart % (repeatLong * 60L) != 0)
jumpCount++;
sTime.add(Calendar.MINUTE, getRepeatInterval() * (int)jumpCount);
fireTime = sTime.getTime();
} else if(repeatUnit.equals(IntervalUnit.HOUR)) {
long jumpCount = secondsAfterStart / (repeatLong * 60L * 60L);
if(secondsAfterStart % (repeatLong * 60L * 60L) != 0)
jumpCount++;
sTime.add(Calendar.HOUR_OF_DAY, getRepeatInterval() * (int)jumpCount);
fireTime = sTime.getTime();
}
// g. Ensure this new fireTime is within the day, or else we need to advance to next day.
if (fireTime.after(fireTimeEndDate)) {
fireTime = advanceToNextDayOfWeekIfNecessary(fireTime, isSameDay(fireTime, fireTimeEndDate));
// make sure we hit the startTimeOfDay on the new day
fireTime = startTimeOfDay.getTimeOfDayForDate(fireTime);
}
// i. Return calculated fireTime.
return fireTime;
}
private boolean isSameDay(Date d1, Date d2) {
Calendar c1 = createCalendarTime(d1);
Calendar c2 = createCalendarTime(d2);
return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}
/**
* Given fireTime time determine if it is on a valid day of week. If so, simply return it unaltered,
* if not, advance to the next valid week day, and set the time of day to the start time of day
*
* @param fireTime - given next fireTime.
* @param forceToAdvanceNextDay - flag to whether to advance day without check existing week day. This scenario
* can happen when a caller determine fireTime has passed the endTimeOfDay that fireTime should move to next day anyway.
* @return a next day fireTime.
*/
private Date advanceToNextDayOfWeekIfNecessary(Date fireTime, boolean forceToAdvanceNextDay) {
// a. Advance or adjust to next dayOfWeek if need to first, starting next day with startTimeOfDay.
TimeOfDay sTimeOfDay = getStartTimeOfDay();
Date fireTimeStartDate = sTimeOfDay.getTimeOfDayForDate(fireTime);
Calendar fireTimeStartDateCal = createCalendarTime(fireTimeStartDate);
int dayOfWeekOfFireTime = fireTimeStartDateCal.get(Calendar.DAY_OF_WEEK);
// b2. We need to advance to another day if isAfterTimePassEndTimeOfDay is true, or dayOfWeek is not set.
Set daysOfWeekToFire = getDaysOfWeek();
if (forceToAdvanceNextDay || !daysOfWeekToFire.contains(dayOfWeekOfFireTime)) {
// Advance one day at a time until next available date.
for(int i=1; i <= 7; i++) {
fireTimeStartDateCal.add(Calendar.DATE, 1);
dayOfWeekOfFireTime = fireTimeStartDateCal.get(Calendar.DAY_OF_WEEK);
if (daysOfWeekToFire.contains(dayOfWeekOfFireTime)) {
fireTime = fireTimeStartDateCal.getTime();
break;
}
}
}
// Check fireTime not pass the endTime
Date eTime = getEndTime();
if (eTime != null && fireTime.getTime() > eTime.getTime()) {
return null;
}
return fireTime;
}
/**
*
* Returns the final time at which the DailyTimeIntervalTrigger
will
* fire, if there is no end time set, null will be returned.
*
*
*
* Note that the return time may be in the past.
*
*/
@Override
public Date getFinalFireTime() {
if (complete || getEndTime() == null) {
return null;
}
// We have an endTime, we still need to check to see if there is a endTimeOfDay if that's applicable.
Date eTime = getEndTime();
if (endTimeOfDay != null) {
Date endTimeOfDayDate = endTimeOfDay.getTimeOfDayForDate(eTime);
if (eTime.getTime() < endTimeOfDayDate.getTime()) {
eTime = endTimeOfDayDate;
}
}
return eTime;
}
/**
*
* Determines whether or not the DailyTimeIntervalTrigger
will occur
* again.
*
*/
@Override
public boolean mayFireAgain() {
return (getNextFireTime() != null);
}
/**
*
* Validates whether the properties of the JobDetail
are
* valid for submission into a Scheduler
.
*
* @throws IllegalStateException
* if a required property (such as Name, Group, Class) is not
* set.
*/
@Override
public void validate() throws SchedulerException {
super.validate();
if (repeatIntervalUnit == null || !(repeatIntervalUnit.equals(IntervalUnit.SECOND) ||
repeatIntervalUnit.equals(IntervalUnit.MINUTE) ||repeatIntervalUnit.equals(IntervalUnit.HOUR)))
throw new SchedulerException("Invalid repeat IntervalUnit (must be SECOND, MINUTE or HOUR).");
if (repeatInterval < 1) {
throw new SchedulerException("Repeat Interval cannot be zero.");
}
// Ensure interval does not exceed 24 hours
long secondsInHour = 24 * 60 * 60L;
if (repeatIntervalUnit == IntervalUnit.SECOND && repeatInterval > secondsInHour) {
throw new SchedulerException("repeatInterval can not exceed 24 hours (" + secondsInHour + " seconds). Given " + repeatInterval);
}
if (repeatIntervalUnit == IntervalUnit.MINUTE && repeatInterval > secondsInHour / 60L) {
throw new SchedulerException("repeatInterval can not exceed 24 hours (" + secondsInHour / 60L + " minutes). Given " + repeatInterval);
}
if (repeatIntervalUnit == IntervalUnit.HOUR && repeatInterval > 24 ) {
throw new SchedulerException("repeatInterval can not exceed 24 hours. Given " + repeatInterval + " hours.");
}
// Ensure timeOfDay is in order.
if (getEndTimeOfDay() != null && !getStartTimeOfDay().before(getEndTimeOfDay())) {
throw new SchedulerException("StartTimeOfDay " + startTimeOfDay + " should not come after endTimeOfDay " + endTimeOfDay);
}
}
/**
* {@inheritDoc}
*/
public Set getDaysOfWeek() {
if (daysOfWeek == null) {
daysOfWeek = DailyTimeIntervalScheduleBuilder.ALL_DAYS_OF_THE_WEEK;
}
return daysOfWeek;
}
public void setDaysOfWeek(Set daysOfWeek) {
if(daysOfWeek == null || daysOfWeek.size() == 0)
throw new IllegalArgumentException("DaysOfWeek set must be a set that contains at least one day.");
else if(daysOfWeek.size() == 0)
throw new IllegalArgumentException("DaysOfWeek set must contain at least one day.");
this.daysOfWeek = daysOfWeek;
}
/**
* {@inheritDoc}
*/
public TimeOfDay getStartTimeOfDay() {
if (startTimeOfDay == null) {
startTimeOfDay = new TimeOfDay(0, 0, 0);
}
return startTimeOfDay;
}
public void setStartTimeOfDay(TimeOfDay startTimeOfDay) {
if (startTimeOfDay == null) {
throw new IllegalArgumentException("Start time of day cannot be null");
}
TimeOfDay eTime = getEndTimeOfDay();
if (eTime != null && eTime.before(startTimeOfDay)) {
throw new IllegalArgumentException(
"End time of day cannot be before start time of day");
}
this.startTimeOfDay = startTimeOfDay;
}
/**
* {@inheritDoc}
*/
public TimeOfDay getEndTimeOfDay() {
return endTimeOfDay;
}
public void setEndTimeOfDay(TimeOfDay endTimeOfDay) {
if (endTimeOfDay == null)
throw new IllegalArgumentException("End time of day cannot be null");
TimeOfDay sTime = getStartTimeOfDay();
if (sTime != null && endTimeOfDay.before(endTimeOfDay)) {
throw new IllegalArgumentException(
"End time of day cannot be before start time of day");
}
this.endTimeOfDay = endTimeOfDay;
}
/**
* Get a {@link ScheduleBuilder} that is configured to produce a
* schedule identical to this trigger's schedule.
*
* @see #getTriggerBuilder()
*/
@Override
public ScheduleBuilder getScheduleBuilder() {
DailyTimeIntervalScheduleBuilder cb = DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
.withInterval(getRepeatInterval(), getRepeatIntervalUnit())
.onDaysOfTheWeek(getDaysOfWeek()).startingDailyAt(getStartTimeOfDay()).endingDailyAt(getEndTimeOfDay());
switch(getMisfireInstruction()) {
case MISFIRE_INSTRUCTION_DO_NOTHING : cb.withMisfireHandlingInstructionDoNothing();
break;
case MISFIRE_INSTRUCTION_FIRE_ONCE_NOW : cb.withMisfireHandlingInstructionFireAndProceed();
break;
}
return cb;
}
/** This trigger has no additional properties besides what's defined in this class. */
public boolean hasAdditionalProperties() {
return false;
}
public int getRepeatCount() {
return repeatCount;
}
public void setRepeatCount(int repeatCount) {
if (repeatCount < 0 && repeatCount != REPEAT_INDEFINITELY) {
throw new IllegalArgumentException("Repeat count must be >= 0, use the " +
"constant REPEAT_INDEFINITELY for infinite.");
}
this.repeatCount = repeatCount;
}
}