/* * (c) Rob Gordon 2005 */ package org.oddjob.scheduling; import java.util.Date; import org.oddjob.arooa.life.ComponentPersistException; import org.oddjob.jobs.GrabJob; import org.oddjob.jobs.job.ResetAction; import org.oddjob.jobs.job.ResetActions; import org.oddjob.persist.ArchiveJob; import org.oddjob.schedules.Interval; import org.oddjob.schedules.IntervalTo; import org.oddjob.schedules.schedules.BrokenSchedule; import org.oddjob.schedules.schedules.CountSchedule; import org.oddjob.schedules.schedules.DailySchedule; import org.oddjob.schedules.schedules.DateSchedule; import org.oddjob.schedules.schedules.DayAfterSchedule; import org.oddjob.schedules.schedules.DayBeforeSchedule; import org.oddjob.schedules.schedules.IntervalSchedule; import org.oddjob.schedules.schedules.MonthlySchedule; import org.oddjob.schedules.schedules.TimeSchedule; import org.oddjob.schedules.schedules.WeeklySchedule; import org.oddjob.schedules.schedules.YearlySchedule; import org.oddjob.state.CompleteOrNotOp; import org.oddjob.state.StateCondition; import org.oddjob.state.StateConditions; import org.oddjob.state.StateOperator; import org.oddjob.values.SetJob; /** * @oddjob.description Provides a simple timer for periodic or once only * execution of the child job. * <p> * * <h4>Schedules</h4> * * Once only execution: * <ul> * <li>{@link TimeSchedule}</li> * <li>{@link DateSchedule}</li> * <li>{@link CountSchedule} (With a count of 1)</li> * </ul> * Recurring executions: * <ul> * <li>{@link YearlySchedule}</li> * <li>{@link MonthlySchedule}</li> * <li>{@link WeeklySchedule}</li> * <li>{@link DailySchedule}</li> * <li>{@link IntervalSchedule}</li> * </ul> * Holidays: * <ul> * <li>{@link BrokenSchedule}</li> * <li>{@link DayAfterSchedule}</li> * <li>{@link DayBeforeSchedule}</li> * </ul> * * <h4>Missed Executions</h4> * <p> * If Oddjob is running with a persister missed executions fire immediately one * after the other until all missed executions have run. * <p> * This can be overridden with the <code>skipMissedRuns</code> property. * <p> * If a timer is started after the initial execution time but within the interval * of the schedule - execution will happen immediately. Extended intervals are created * using the <code>from</code> properties instead of the <code>at/in/on</code> * properties of schedules. * * <h4>Changing The Next Due Time</h4> * * There are two ways to change the next due date of a timer. They both * require that the timer has been started but is not yet executing, and they * both involve dynamically setting properties of the job which can be done * via the 'Job' -> 'Set Property' menu item in Oddjob Explorer or via * the {@link SetJob} job within Oddjob. * <p> * The first method is to set the next due date directly with the * <code>nextDue</code> property. The existing timer is cancelled and the * job rescheduled to run at this time. If the time is in the past, the job * will run immediately. * </p> * The second method is to set the the <code>reschedule</code> property with * a date and time. The next due date is calculated by applying the date * and time the schedule. This is particularly useful for advancing a * timer. * * <h4>Retrying Failed Jobs</h4> * * Nest a {@link Retry} job. * * <h4>Recording the Outcome of Runs</h4> * * Nest an {@link ArchiveJob}. * * <h4>Distributed Scheduling</h4> * * Nest a {@link GrabJob}. * * <h4>For More Information</h4> * * For more information see the Scheduling section of the User Guide. * * * @oddjob.example * * A Timer that runs at 10am each day, Monday to Friday. * * {@oddjob.xml.resource org/oddjob/scheduling/TimerExample.xml} * * @oddjob.example * * Run once at 10am or any time after. * * {@oddjob.xml.resource org/oddjob/scheduling/TimerOnceExample.xml} * * If the report completes before 10am the timer will schedule it to be e-mailed * at 10am. If the report completes after 10am it is e-mailed immediately. * * @oddjob.example * * Use a timer to stop a long running job. * * {@oddjob.xml.resource org/oddjob/scheduling/TimerStopJobExample.xml} * * The job will be stopped after 10 seconds. If the job has already completed * the stop will have no affect. * * @oddjob.example * * Manually setting the next due date of the timer. When the set job is * run manually the job will be schedule to run at the new time. * * {@oddjob.xml.resource org/oddjob/scheduling/TimerSetNextDueExample.xml} * * Note that the <code>current<code> interval property is not changed, so * the echo job shows 'Running at 9999-12-31 00:00:00.000'. * * @oddjob.example * * Manually rescheduling the timer. When the set job is run manually, the * timer will advance to it's next scheduled slot. * * {@oddjob.xml.resource org/oddjob/scheduling/TimerSetRescheduleExample.xml} * * Note that the unlike above, <code>current<code> interval property * changes when the time is rescheduled. * * @author Rob Gordon. */ public class Timer extends TimerBase { private static final long serialVersionUID = 2009091420120126L; /** * @oddjob.property * @oddjob.description Don't reschedule if the scheduled job doesn't * complete. * @oddjob.required No. */ private boolean haltOnFailure; /** * @oddjob.property * @oddjob.description Use the current time, not the last completed time * to calculate when the job is next due. * @oddjob.required No. */ private boolean skipMissedRuns; @Override protected StateOperator getStateOp() { return new CompleteOrNotOp(); } @Override protected void begin() throws ComponentPersistException { super.begin(); Date currentTime = getClock().getDate(); Interval currentInterval = getCurrent(); boolean skipMissedRuns = isSkipMissedRuns(); if (currentInterval != null && (!skipMissedRuns || skipMissedRuns && currentTime.before( currentInterval.getToDate()))) { logger().info("Setting next due from value of last current property."); internalSetNextDue(currentInterval.getFromDate()); } else { logger().info("Calculating schedule from current clock date time."); scheduleFrom(currentTime); } } public void setHaltOnFailure(boolean haltOnFailure) { this.haltOnFailure = true; } public boolean isHaltOnFailure() { return haltOnFailure; } public boolean isSkipMissedRuns() { return skipMissedRuns; } public void setSkipMissedRuns(boolean skipMissedRuns) { this.skipMissedRuns = skipMissedRuns; } @Override protected IntervalTo getLimits() { return null; } @Override protected StateCondition getDefaultHaltOn() { if (haltOnFailure) { return StateConditions.FAILURE; } else { return StateConditions.NONE; } } @Override protected ResetAction getDefaultReset() { return ResetActions.HARD; } }