org.quartz.triggers.SimpleTriggerImpl Maven / Gradle / Ivy
Show all versions of sundial Show documentation
/**
* Copyright 2015 Knowm Inc. (http://knowm.org) and contributors.
* Copyright 2013-2015 Xeiam LLC (http://xeiam.com) and contributors.
* Copyright 2001-2011 Terracotta Inc. (http://terracotta.org).
*
* 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.triggers;
import java.util.Date;
import org.quartz.core.Calendar;
import org.quartz.core.JobExecutionContext;
import org.quartz.core.Scheduler;
import org.quartz.exceptions.JobExecutionException;
import org.quartz.exceptions.SchedulerException;
/**
*
* A concrete {@link Trigger}
that is used to fire a {@link org.quartz.jobs.JobDetail}
at a given moment in time, and
* optionally repeated at a specified interval.
*
*
* @author James House
* @author contributions by Lieven Govaerts of Ebitec Nv, Belgium.
*/
public class SimpleTriggerImpl extends AbstractTrigger implements SimpleTrigger {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Constants.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Data members.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private int repeatCount = 0;
private long repeatInterval = 0;
private int timesTriggered = 0;
private final boolean complete = false;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Constructors.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Create a SimpleTrigger
with no settings.
*
*/
public SimpleTriggerImpl() {
super();
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Interface.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Get the time at which the SimpleTrigger
should occur.
*
*/
@Override
public Date getStartTime() {
return startTime;
}
/**
*
* Set the time at which the SimpleTrigger
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 && startTime != null && eTime.before(startTime)) {
throw new IllegalArgumentException("End time cannot be before start time");
}
this.startTime = startTime;
}
/**
*
* Get the time at which the SimpleTrigger
should quit repeating - even if repeastCount isn't yet satisfied.
*
*
* @see #getFinalFireTime()
*/
@Override
public Date getEndTime() {
return endTime;
}
/**
*
* Set the time at which the SimpleTrigger
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;
}
@Override
public int getRepeatCount() {
return repeatCount;
}
/**
*
* Set the the number of time the SimpleTrigger
should repeat, after which it will be automatically deleted.
*
*
* @see #REPEAT_INDEFINITELY
* @exception IllegalArgumentException if repeatCount is < 0
*/
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;
}
@Override
public long getRepeatInterval() {
return repeatInterval;
}
/**
*
* Set the the time interval (in milliseconds) at which the SimpleTrigger
should repeat.
*
*
* @exception IllegalArgumentException if repeatInterval is <= 0
*/
public void setRepeatInterval(long repeatInterval) {
if (repeatInterval < 0) {
throw new IllegalArgumentException("Repeat interval must be >= 0");
}
this.repeatInterval = repeatInterval;
}
/**
*
* Get the number of times the SimpleTrigger
has already fired.
*
*/
@Override
public int getTimesTriggered() {
return timesTriggered;
}
/**
*
* Set the number of times the SimpleTrigger
has already fired.
*
*/
public void setTimesTriggered(int timesTriggered) {
this.timesTriggered = timesTriggered;
}
@Override
protected boolean validateMisfireInstruction(int misfireInstruction) {
if (misfireInstruction < MISFIRE_INSTRUCTION_SMART_POLICY) {
return false;
}
if (misfireInstruction > MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
return false;
}
return true;
}
/**
*
* Updates the SimpleTrigger
's state based on the MISFIRE_INSTRUCTION_XXX that was selected when the SimpleTrigger
was
* created.
*
*
* If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY, then the following scheme will be used:
*
* - If the Repeat Count is
0
, then the instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_NOW
.
* - If the Repeat Count is
REPEAT_INDEFINITELY
, then the instruction will be interpreted as
* MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
. WARNING: using
* MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT with a trigger that has a non-null end-time may cause the trigger to never fire again if
* the end-time arrived during the misfire time span.
* - If the Repeat Count is
> 0
, then the instruction will be interpreted as
* MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
.
*
*
*
* If the misfire instruction is set to Trigger.MISFIRE_INSTRUCTION_SKIP_TO_NEXT_FIRE_AFTER_CURRENT_DATE
then the behavior will be
* identical to
*/
@Override
public void updateAfterMisfire(Calendar cal) {
int instr = getMisfireInstruction();
if (instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
return;
}
if (instr == Trigger.MISFIRE_INSTRUCTION_SMART_POLICY) {
if (getRepeatCount() == 0) {
instr = MISFIRE_INSTRUCTION_FIRE_NOW;
} else if (getRepeatCount() == REPEAT_INDEFINITELY) {
instr = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;
} else {
// if (getRepeatCount() > 0)
instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;
}
} else if (instr == MISFIRE_INSTRUCTION_FIRE_NOW && getRepeatCount() != 0) {
instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT;
}
if (instr == MISFIRE_INSTRUCTION_FIRE_NOW) {
setNextFireTime(new Date());
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null && !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
if (newFireTime == null) {
break;
}
// avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(newFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
newFireTime = null;
}
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null && !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
if (newFireTime == null) {
break;
}
// avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(newFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
newFireTime = null;
}
}
if (newFireTime != null) {
int timesMissed = computeNumTimesFiredBetween(nextFireTime, newFireTime);
setTimesTriggered(getTimesTriggered() + timesMissed);
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) {
Date newFireTime = new Date();
if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {
setRepeatCount(getRepeatCount() - getTimesTriggered());
setTimesTriggered(0);
}
if (getEndTime() != null && getEndTime().before(newFireTime)) {
setNextFireTime(null); // We are past the end time
} else {
setStartTime(newFireTime);
setNextFireTime(newFireTime);
}
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) {
Date newFireTime = new Date();
int timesMissed = computeNumTimesFiredBetween(nextFireTime, newFireTime);
if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {
int remainingCount = getRepeatCount() - (getTimesTriggered() + timesMissed);
if (remainingCount <= 0) {
remainingCount = 0;
}
setRepeatCount(remainingCount);
setTimesTriggered(0);
}
if (getEndTime() != null && getEndTime().before(newFireTime)) {
setNextFireTime(null); // We are past the end time
} else {
setStartTime(newFireTime);
setNextFireTime(newFireTime);
}
}
}
/**
*
* 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(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;
}
}
}
/**
*
* 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(Calendar calendar) {
nextFireTime = getStartTime();
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;
}
/**
*
* 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.
*
*
* @see TriggerUtils#computeFireTimesBetween(Trigger, Calendar, Date, Date)
*/
@Override
public Date getNextFireTime() {
return nextFireTime;
}
/**
*
* Returns the previous time at which the SimpleTrigger
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 SimpleTrigger
should fire.
*
*
* This method should not be invoked by client code.
*
*/
@Override
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
/**
*
* Set the previous time at which the SimpleTrigger
fired.
*
*
* This method should not be invoked by client code.
*
*/
@Override
public void setPreviousFireTime(Date previousFireTime) {
this.previousFireTime = previousFireTime;
}
/**
*
* Returns the next time at which the SimpleTrigger
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) {
if (complete) {
return null;
}
if ((timesTriggered > repeatCount) && (repeatCount != REPEAT_INDEFINITELY)) {
return null;
}
if (afterTime == null) {
afterTime = new Date();
}
if (repeatCount == 0 && afterTime.compareTo(getStartTime()) >= 0) {
return null;
}
long startMillis = getStartTime().getTime();
long afterMillis = afterTime.getTime();
long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime().getTime();
if (endMillis <= afterMillis) {
return null;
}
if (afterMillis < startMillis) {
return new Date(startMillis);
}
long numberOfTimesExecuted = ((afterMillis - startMillis) / repeatInterval) + 1;
if ((numberOfTimesExecuted > repeatCount) && (repeatCount != REPEAT_INDEFINITELY)) {
return null;
}
Date time = new Date(startMillis + (numberOfTimesExecuted * repeatInterval));
if (endMillis <= time.getTime()) {
return null;
}
return time;
}
/**
*
* Returns the last time at which the SimpleTrigger
will fire, before the given time. If the trigger will not fire before the given
* time, null
will be returned.
*
*/
private Date getFireTimeBefore(Date end) {
if (end.getTime() < getStartTime().getTime()) {
return null;
}
int numFires = computeNumTimesFiredBetween(getStartTime(), end);
return new Date(getStartTime().getTime() + (numFires * repeatInterval));
}
private int computeNumTimesFiredBetween(Date start, Date end) {
if (repeatInterval < 1) {
return 0;
}
long time = end.getTime() - start.getTime();
return (int) (time / repeatInterval);
}
/**
*
* Returns the final time at which the SimpleTrigger
will fire, if repeatCount is REPEAT_INDEFINITELY, null will be returned.
*
*
* Note that the return time may be in the past.
*
*/
@Override
public Date getFinalFireTime() {
if (repeatCount == 0) {
return startTime;
}
if (repeatCount == REPEAT_INDEFINITELY) {
return (getEndTime() == null) ? null : getFireTimeBefore(getEndTime());
}
long lastTrigger = startTime.getTime() + (repeatCount * repeatInterval);
if ((getEndTime() == null) || (lastTrigger < getEndTime().getTime())) {
return new Date(lastTrigger);
} else {
return getFireTimeBefore(getEndTime());
}
}
/**
*
* Determines whether or not the SimpleTrigger
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 (repeatCount != 0 && repeatInterval < 1) {
throw new SchedulerException("Repeat Interval cannot be zero.");
}
}
@Override
public String toString() {
return super.toString() + ", repeatCount: " + getRepeatCount() + ", repeatInterval: " + getRepeatInterval();
}
}