org.quartz.Trigger 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.
*
*/
/*
* Previously Copyright (c) 2001-2004 James House
*/
package org.quartz;
import java.util.Date;
import java.util.LinkedList;
/**
*
* The base abstract class to be extended by all Trigger
s.
*
*
*
* Triggers
s have a name and group associated with them, which
* should uniquely identify them within a single {@link Scheduler}
.
*
*
*
* Trigger
s are the 'mechanism' by which Job
s
* are scheduled. Many Trigger
s can point to the same Job
,
* but a single Trigger
can only point to one Job
.
*
*
*
* Triggers can 'send' parameters/data to Job
s by placing contents
* into the JobDataMap
on the Trigger
.
*
*
* @see SimpleTrigger
* @see CronTrigger
* @see NthIncludedDayTrigger
* @see TriggerUtils
* @see JobDataMap
* @see JobExecutionContext
*
* @author James House
* @author Sharada Jambula
*/
public abstract class Trigger implements java.io.Serializable, Cloneable,
Comparable {
private static final long serialVersionUID = -3904243490805975570L;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constants.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Instructs the {@link Scheduler}
that the {@link Trigger}
* has no further instructions.
*
*/
public static final int INSTRUCTION_NOOP = 0;
/**
*
* Instructs the {@link Scheduler}
that the {@link Trigger}
* wants the {@link org.quartz.JobDetail}
to re-execute
* immediately. If not in a 'RECOVERING' or 'FAILED_OVER' situation, the
* execution context will be re-used (giving the Job
the
* abilitiy to 'see' anything placed in the context by its last execution).
*
*/
public static final int INSTRUCTION_RE_EXECUTE_JOB = 1;
/**
*
* Instructs the {@link Scheduler}
that the {@link Trigger}
* should be put in the COMPLETE
state.
*
*/
public static final int INSTRUCTION_SET_TRIGGER_COMPLETE = 2;
/**
*
* Instructs the {@link Scheduler}
that the {@link Trigger}
* wants itself deleted.
*
*/
public static final int INSTRUCTION_DELETE_TRIGGER = 3;
/**
*
* Instructs the {@link Scheduler}
that all Trigger
* s referencing the same {@link org.quartz.JobDetail}
as
* this one should be put in the COMPLETE
state.
*
*/
public static final int INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE = 4;
/**
*
* Instructs the {@link Scheduler}
that all Trigger
* s referencing the same {@link org.quartz.JobDetail}
as
* this one should be put in the ERROR
state.
*
*/
public static final int INSTRUCTION_SET_TRIGGER_ERROR = 5;
/**
*
* Instructs the {@link Scheduler}
that the Trigger
* should be put in the ERROR
state.
*
*/
public static final int INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR = 6;
/**
*
* Instructs the {@link Scheduler}
that upon a mis-fire
* situation, the updateAfterMisfire()
method will be called
* on the Trigger
to determine the mis-fire instruction.
*
*
*
* In order to see if this instruction fits your needs, you should look at
* the documentation for the getSmartMisfirePolicy()
method
* on the particular Trigger
implementation you are using.
*
*/
public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;
/**
*
* Indicates that the Trigger
is in the "normal" state.
*
*/
public final static int STATE_NORMAL = 0;
/**
*
* Indicates that the Trigger
is in the "paused" state.
*
*/
public final static int STATE_PAUSED = 1;
/**
*
* Indicates that the Trigger
is in the "complete" state.
*
*
*
* "Complete" indicates that the trigger has not remaining fire-times in
* its schedule.
*
*/
public final static int STATE_COMPLETE = 2;
/**
*
* Indicates that the Trigger
is in the "error" state.
*
*
*
* A Trigger
arrives at the error state when the scheduler
* attempts to fire it, but cannot due to an error creating and executing
* its related job. Often this is due to the Job
's
* class not existing in the classpath.
*
*
*
* When the trigger is in the error state, the scheduler will make no
* attempts to fire it.
*
*/
public final static int STATE_ERROR = 3;
/**
*
* Indicates that the Trigger
is in the "blocked" state.
*
*
*
* A Trigger
arrives at the blocked state when the job that
* it is associated with is a StatefulJob
and it is
* currently executing.
*
*
* @see StatefulJob
*/
public final static int STATE_BLOCKED = 4;
/**
*
* Indicates that the Trigger
does not exist.
*
*/
public final static int STATE_NONE = -1;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private String name;
private String group = Scheduler.DEFAULT_GROUP;
private String jobName;
private String jobGroup = Scheduler.DEFAULT_GROUP;
private String description;
private JobDataMap jobDataMap;
private boolean volatility = false;
private String calendarName = null;
private String fireInstanceId = null;
private int misfireInstruction = MISFIRE_INSTRUCTION_SMART_POLICY;
private LinkedList triggerListeners = new LinkedList();
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Create a Trigger
with no specified name, group, or {@link org.quartz.JobDetail}
.
*
*
*
* Note that the {@link #setName(String)},{@link #setGroup(String)}and
* the {@link #setJobName(String)}and {@link #setJobGroup(String)}methods
* must be called before the Trigger
can be placed into a
* {@link Scheduler}.
*
*/
public Trigger() {
// do nothing...
}
/**
*
* Create a Trigger
with the given name, and group.
*
*
*
* Note that the {@link #setJobName(String)}and
* {@link #setJobGroup(String)}methods must be called before the Trigger
* can be placed into a {@link Scheduler}.
*
*
* @param group if null
, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if name is null or empty, or the group is an empty string.
*/
public Trigger(String name, String group) {
setName(name);
setGroup(group);
}
/**
*
* Create a Trigger
with the given name, and group.
*
*
* @param group if null
, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if name is null or empty, or the group is an empty string.
*/
public Trigger(String name, String group, String jobName, String jobGroup) {
setName(name);
setGroup(group);
setJobName(jobName);
setJobGroup(jobGroup);
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
*
* Get the name of this Trigger
.
*
*/
public String getName() {
return name;
}
/**
*
* Set the name of this Trigger
.
*
*
* @exception IllegalArgumentException
* if name is null or empty.
*/
public void setName(String name) {
if (name == null || name.trim().length() == 0)
throw new IllegalArgumentException(
"Trigger name cannot be null or empty.");
this.name = name;
}
/**
*
* Get the group of this Trigger
.
*
*/
public String getGroup() {
return group;
}
/**
*
* Set the name of this Trigger
.
*
*
* @param group if null
, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if group is an empty string.
*/
public void setGroup(String group) {
if (group != null && group.trim().length() == 0)
throw new IllegalArgumentException(
"Group name cannot be an empty string.");
if(group == null)
group = Scheduler.DEFAULT_GROUP;
this.group = group;
}
/**
*
* Get the name of the associated {@link org.quartz.JobDetail}
.
*
*/
public String getJobName() {
return jobName;
}
/**
*
* Set the name of the associated {@link org.quartz.JobDetail}
.
*
*
* @exception IllegalArgumentException
* if jobName is null or empty.
*/
public void setJobName(String jobName) {
if (jobName == null || jobName.trim().length() == 0)
throw new IllegalArgumentException(
"Job name cannot be null or empty.");
this.jobName = jobName;
}
/**
*
* Get the name of the associated {@link org.quartz.JobDetail}
's
* group.
*
*/
public String getJobGroup() {
return jobGroup;
}
/**
*
* Set the name of the associated {@link org.quartz.JobDetail}
's
* group.
*
*
* @param group if null
, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if group is an empty string.
*/
public void setJobGroup(String jobGroup) {
if (jobGroup != null && jobGroup.trim().length() == 0)
throw new IllegalArgumentException(
"Group name cannot be null or empty.");
if(jobGroup == null)
jobGroup = Scheduler.DEFAULT_GROUP;
this.jobGroup = jobGroup;
}
/**
*
* Returns the 'full name' of the Trigger
in the format
* "group.name".
*
*/
public String getFullName() {
return group + "." + name;
}
/**
*
* Returns the 'full name' of the Job
that the Trigger
* points to, in the format "group.name".
*
*/
public String getFullJobName() {
return jobGroup + "." + jobName;
}
/**
*
* Return the description given to the Trigger
instance by
* its creator (if any).
*
*
* @return null if no description was set.
*/
public String getDescription() {
return description;
}
/**
*
* Set a description for the Trigger
instance - may be
* useful for remembering/displaying the purpose of the trigger, though the
* description has no meaning to Quartz.
*
*/
public void setDescription(String description) {
this.description = description;
}
/**
*
* Set whether or not the Trigger
should be persisted in the
* {@link org.quartz.spi.JobStore}
for re-use after program
* restarts.
*
*/
public void setVolatility(boolean volatility) {
this.volatility = volatility;
}
/**
*
* Associate the {@link Calendar}
with the given name with
* this Trigger.
*
*
* @param calendarName
* use null
to dis-associate a Calendar.
*/
public void setCalendarName(String calendarName) {
this.calendarName = calendarName;
}
/**
*
* Get the name of the {@link Calendar}
associated with this
* Trigger.
*
*
* @return null
if there is no associated Calendar.
*/
public String getCalendarName() {
return calendarName;
}
/**
*
* Get the JobDataMap
that is associated with the
* Trigger
.
*
*
*
* Changes made to this map during job execution are not re-persisted, and
* in fact typically result in an IllegalStateException
.
*
*/
public JobDataMap getJobDataMap() {
if (jobDataMap == null) jobDataMap = new JobDataMap();
return jobDataMap;
}
/**
*
* Set the JobDataMap
to be associated with the
* Trigger
.
*
*/
public void setJobDataMap(JobDataMap jobDataMap) {
this.jobDataMap = jobDataMap;
}
/**
*
* Whether or not the Trigger
should be persisted in the
* {@link org.quartz.spi.JobStore}
for re-use after program
* restarts.
*
*
*
* If not explicitly set, the default value is false
.
*
*
* @return true
if the Trigger
should be
* garbage collected along with the {@link Scheduler}
.
*/
public boolean isVolatile() {
return volatility;
}
/**
*
* Add the specified name of a {@link TriggerListener}
to
* the end of the Trigger
's list of listeners.
*
*/
public void addTriggerListener(String name) {
triggerListeners.add(name);
}
/**
*
* Remove the specified name of a {@link TriggerListener}
* from the Trigger
's list of listeners.
*
*
* @return true if the given name was found in the list, and removed
*/
public boolean removeTriggerListener(String name) {
return triggerListeners.remove(name);
}
/**
*
* Returns an array of String
s containing the names of all
* {@link TriggerListener}
assigned to the Trigger
,
* in the order in which they should be notified.
*
*/
public String[] getTriggerListenerNames() {
String[] outNames = new String[triggerListeners.size()];
return (String[]) triggerListeners.toArray(outNames);
}
/**
*
* This method should not be used by the Quartz client.
*
*
*
* 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)
*/
public abstract void triggered(Calendar calendar);
/**
*
* This method should not be used by the Quartz client.
*
*
*
* 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
).
*
*/
public abstract Date computeFirstFireTime(Calendar calendar);
/**
*
* This method should not be used by the Quartz client.
*
*
*
* Called after the {@link Scheduler}
has executed the
* {@link org.quartz.JobDetail}
associated with the Trigger
* in order to get the final instruction code from the trigger.
*
*
* @param context
* is the JobExecutionContext
that was used by the
* Job
'sexecute(xx)
method.
* @param result
* is the JobExecutionException
thrown by the
* Job
, if any (may be null).
* @return one of the Trigger.INSTRUCTION_XXX constants.
*
* @see #INSTRUCTION_NOOP
* @see #INSTRUCTION_RE_EXECUTE_JOB
* @see #INSTRUCTION_DELETE_TRIGGER
* @see #INSTRUCTION_SET_TRIGGER_COMPLETE
* @see #triggered(Calendar)
*/
public abstract int executionComplete(JobExecutionContext context,
JobExecutionException result);
/**
*
* Used by the {@link 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 {@link org.quartz.spi.JobStore}
.
*
*/
public abstract boolean mayFireAgain();
/**
*
* Get the time at which the Trigger
should occur.
*
*/
public abstract Date getStartTime();
public abstract void setStartTime(Date startTime);
public abstract void setEndTime(Date endTime);
/**
*
* Get the time at which the Trigger
should quit repeating -
* even if an assigned 'repeatCount' isn't yet satisfied.
*
*
* @see #getFinalFireTime()
*/
public abstract Date getEndTime();
/**
*
* Returns the next time at which the Trigger
will fire. If
* the trigger will not fire again, null
will be returned.
* The value returned is not guaranteed to be valid until after the Trigger
* has been added to the scheduler.
*
*/
public abstract Date getNextFireTime();
/**
*
* Returns the previous time at which the Trigger
will fire.
* If the trigger has not yet fired, null
will be returned.
*/
public abstract Date getPreviousFireTime();
/**
*
* Returns the next time at which the Trigger
will fire,
* after the given time. If the trigger will not fire after the given time,
* null
will be returned.
*
*/
public abstract Date getFireTimeAfter(Date afterTime);
/**
*
* Returns the last time at which the Trigger
will fire, if
* the Trigger will repeat indefinitely, null will be returned.
*
*
*
* Note that the return time *may* be in the past.
*
*/
public abstract Date getFinalFireTime();
/**
*
* Set the instruction the Scheduler
should be given for
* handling misfire situations for this Trigger
- the
* concrete Trigger
type that you are using will have
* defined a set of additional MISFIRE_INSTRUCTION_XXX
* constants that may be passed to this method.
*
*
*
* If not explicitly set, the default value is MISFIRE_INSTRUCTION_SMART_POLICY
.
*
*
* @see #MISFIRE_INSTRUCTION_SMART_POLICY
* @see #updateAfterMisfire(Calendar)
* @see SimpleTrigger
* @see CronTrigger
*/
public void setMisfireInstruction(int misfireInstruction) {
if (!validateMisfireInstruction(misfireInstruction))
throw new IllegalArgumentException(
"The misfire instruction code is invalid for this type of trigger.");
this.misfireInstruction = misfireInstruction;
}
protected abstract boolean validateMisfireInstruction(int misfireInstruction);
/**
*
* Get the instruction the Scheduler
should be given for
* handling misfire situations for this Trigger
- the
* concrete Trigger
type that you are using will have
* defined a set of additional MISFIRE_INSTRUCTION_XXX
* constants that may be passed to this method.
*
*
*
* If not explicitly set, the default value is MISFIRE_INSTRUCTION_SMART_POLICY
.
*
*
* @see #MISFIRE_INSTRUCTION_SMART_POLICY
* @see #updateAfterMisfire(Calendar)
* @see SimpleTrigger
* @see CronTrigger
*/
public int getMisfireInstruction() {
return misfireInstruction;
}
/**
*
* This method should not be used by the Quartz client.
*
*
*
* To be implemented by the concrete classes that extend this class.
*
*
*
* The implementation should update the Trigger
's state
* based on the MISFIRE_INSTRUCTION_XXX that was selected when the Trigger
* was created.
*
*/
public abstract void updateAfterMisfire(Calendar cal);
/**
*
* This method should not be used by the Quartz client.
*
*
*
* To be implemented by the concrete class.
*
*
*
* The implementation should update the Trigger
's state
* based on the given new version of the associated Calendar
* (the state should be updated so that it's next fire time is appropriate
* given the Calendar's new settings).
*
*
* @param cal
*/
public abstract void updateWithNewCalendar(Calendar cal, long misfireThreshold);
/**
*
* 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.
*/
public void validate() throws SchedulerException {
if (name == null)
throw new SchedulerException("Trigger's name cannot be null",
SchedulerException.ERR_CLIENT_ERROR);
if (group == null)
throw new SchedulerException("Trigger's group cannot be null",
SchedulerException.ERR_CLIENT_ERROR);
if (jobName == null)
throw new SchedulerException(
"Trigger's related Job's name cannot be null",
SchedulerException.ERR_CLIENT_ERROR);
if (jobGroup == null)
throw new SchedulerException(
"Trigger's related Job's group cannot be null",
SchedulerException.ERR_CLIENT_ERROR);
}
/**
*
* This method should not be used by the Quartz client.
*
*
*
* Usable by {@link org.quartz.spi.JobStore}
* implementations, in order to facilitate 'recognizing' instances of fired
* Trigger
s as their jobs complete execution.
*
*
*
*/
public void setFireInstanceId(String id) {
this.fireInstanceId = id;
}
/**
*
* This method should not be used by the Quartz client.
*
*/
public String getFireInstanceId() {
return fireInstanceId;
}
/**
*
* Return a simple string representation of this object.
*
*/
public String toString() {
return "Trigger '" + getFullName() + "': triggerClass: '"
+ getClass().getName() + " isVolatile: " + isVolatile()
+ " calendar: '" + getCalendarName() + "' misfireInstruction: "
+ getMisfireInstruction() + " nextFireTime: " + getNextFireTime();
}
/**
*
* Compare the next fire time of this Trigger
to that of
* another.
*
*/
public int compareTo(Object obj) {
Trigger other = (Trigger) obj;
Date myTime = getNextFireTime();
Date otherTime = other.getNextFireTime();
if (myTime == null && otherTime == null) return 0;
if (myTime == null) return 1;
if (otherTime == null) return -1;
if(myTime.before(otherTime))
return -1;
if(myTime.after(otherTime))
return 1;
return 0;
}
public boolean equals(Object obj) {
if (!(obj instanceof Trigger)) return false;
Trigger other = (Trigger) obj;
if (!other.getName().equals(getName())) return false;
if (!other.getGroup().equals(getGroup())) return false;
return true;
}
public int hashCode() {
return getFullName().hashCode();
}
public Object clone() {
Trigger copy;
try {
copy = (Trigger) super.clone();
} catch (CloneNotSupportedException ex) {
throw new IncompatibleClassChangeError("Not Cloneable.");
}
return copy;
}
}