/// <summary> /// Returns a list of Dates that are the next fire times of a /// <see cref="Trigger" />. /// The input trigger will be cloned before any work is done, so you need /// not worry about its state being altered by this method. /// </summary> /// <param name="trigg">The trigger upon which to do the work</param> /// <param name="cal">The calendar to apply to the trigger's schedule</param> /// <param name="numTimes">The number of next fire times to produce</param> /// <returns>List of java.util.Date objects</returns> public static IList ComputeFireTimes(Trigger trigg, ICalendar cal, int numTimes) { ArrayList lst = new ArrayList(); Trigger t = (Trigger)trigg.Clone(); if (t.GetNextFireTimeUtc() == null || !t.GetNextFireTimeUtc().HasValue) { t.ComputeFirstFireTimeUtc(cal); } for (int i = 0; i < numTimes; i++) { NullableDateTime d = t.GetNextFireTimeUtc(); if (d.HasValue) { lst.Add(d); t.Triggered(cal); } else { break; } } return(ArrayList.ReadOnly(new ArrayList(lst))); }
/// <summary> /// Compare the next fire time of this <see cref="Trigger" /> to that of /// another. /// </summary> public virtual int CompareTo(object obj) { Trigger other = (Trigger)obj; NullableDateTime myTime = GetNextFireTimeUtc(); NullableDateTime otherTime = other.GetNextFireTimeUtc(); if (!myTime.HasValue && !otherTime.HasValue) { return(0); } if (!myTime.HasValue) { return(1); } if (!otherTime.HasValue) { return(-1); } if ((myTime.Value < otherTime.Value)) { return(-1); } if ((myTime.Value > otherTime.Value)) { return(1); } return(0); }
/// <summary> /// Returns a list of Dates that are the next fire times of a <see cref="Trigger" /> /// that fall within the given date range. The input trigger will be cloned /// before any work is done, so you need not worry about its state being /// altered by this method. /// <p> /// NOTE: if this is a trigger that has previously fired within the given /// date range, then firings which have already occured will not be listed /// in the output List. /// </p> /// </summary> /// <param name="trigg">The trigger upon which to do the work</param> /// <param name="cal">The calendar to apply to the trigger's schedule</param> /// <param name="from">The starting date at which to find fire times</param> /// <param name="to">The ending date at which to stop finding fire times</param> /// <returns>List of java.util.Date objects</returns> public static IList ComputeFireTimesBetween(Trigger trigg, ICalendar cal, DateTime from, DateTime to) { ArrayList lst = new ArrayList(); Trigger t = (Trigger)trigg.Clone(); if (t.GetNextFireTimeUtc() == null || !t.GetNextFireTimeUtc().HasValue) { t.StartTimeUtc = from; t.EndTimeUtc = to; t.ComputeFirstFireTimeUtc(cal); } // TODO: this method could be more efficient by using logic specific // to the type of trigger ... while (true) { NullableDateTime d = t.GetNextFireTimeUtc(); if (d.HasValue) { if (d.Value < from) { t.Triggered(cal); continue; } if (d.Value > to) { break; } lst.Add(d); t.Triggered(cal); } else { break; } } return(ArrayList.ReadOnly(new ArrayList(lst))); }
/// <summary> /// Remove (delete) the <see cref="Trigger" /> with the /// given name, and store the new given one - which must be associated /// with the same job. /// </summary> /// <param name="ctxt">The scheduling context.</param> /// <param name="triggerName">The name of the <see cref="Trigger" /> to be removed.</param> /// <param name="groupName">The group name of the <see cref="Trigger" /> to be removed.</param> /// <param name="newTrigger">The new <see cref="Trigger" /> to be stored.</param> /// <returns> /// <see langword="null" /> if a <see cref="Trigger" /> with the given /// name and group was not found and removed from the store, otherwise /// the first fire time of the newly scheduled trigger. /// </returns> public virtual NullableDateTime RescheduleJob(SchedulingContext ctxt, string triggerName, string groupName, Trigger newTrigger) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } newTrigger.Validate(); ICalendar cal = null; if (newTrigger.CalendarName != null) { cal = resources.JobStore.RetrieveCalendar(ctxt, newTrigger.CalendarName); } NullableDateTime ft = newTrigger.ComputeFirstFireTimeUtc(cal); if (!ft.HasValue) { throw new SchedulerException("Based on configured schedule, the given trigger will never fire.", SchedulerException.ErrorClientError); } if (resources.JobStore.ReplaceTrigger(ctxt, triggerName, groupName, newTrigger)) { NotifySchedulerThread(newTrigger.GetNextFireTimeUtc()); NotifySchedulerListenersUnscheduled(triggerName, groupName); NotifySchedulerListenersScheduled(newTrigger); } else { return null; } return ft; }
/// <summary> /// Schedule the given <see cref="Trigger" /> with the /// <see cref="IJob" /> identified by the <see cref="Trigger" />'s settings. /// </summary> public virtual DateTime ScheduleJob(SchedulingContext ctxt, Trigger trigger) { ValidateState(); if (trigger == null) { throw new SchedulerException("Trigger cannot be null", SchedulerException.ErrorClientError); } trigger.Validate(); ICalendar cal = null; if (trigger.CalendarName != null) { cal = resources.JobStore.RetrieveCalendar(ctxt, trigger.CalendarName); if (cal == null) { throw new SchedulerException(string.Format(CultureInfo.InvariantCulture, "Calendar not found: {0}", trigger.CalendarName), SchedulerException.ErrorPersistenceCalendarDoesNotExist); } } NullableDateTime ft = trigger.ComputeFirstFireTimeUtc(cal); if (!ft.HasValue) { throw new SchedulerException("Based on configured schedule, the given trigger will never fire.", SchedulerException.ErrorClientError); } resources.JobStore.StoreTrigger(ctxt, trigger, false); NotifySchedulerThread(trigger.GetNextFireTimeUtc()); NotifySchedulerListenersScheduled(trigger); return ft.Value; }
/// <summary> /// Add the <see cref="IJob" /> identified by the given /// <see cref="JobDetail" /> to the Scheduler, and /// associate the given <see cref="Trigger" /> with it. /// <p> /// If the given Trigger does not reference any <see cref="IJob" />, then it /// will be set to reference the Job passed with it into this method. /// </p> /// </summary> public virtual DateTime ScheduleJob(SchedulingContext ctxt, JobDetail jobDetail, Trigger trigger) { ValidateState(); if (jobDetail == null) { throw new SchedulerException("JobDetail cannot be null", SchedulerException.ErrorClientError); } if (trigger == null) { throw new SchedulerException("Trigger cannot be null", SchedulerException.ErrorClientError); } jobDetail.Validate(); if (trigger.JobName == null) { trigger.JobName = jobDetail.Name; trigger.JobGroup = jobDetail.Group; } else if (trigger.JobName != null && !trigger.JobName.Equals(jobDetail.Name)) { throw new SchedulerException("Trigger does not reference given job!", SchedulerException.ErrorClientError); } else if (trigger.JobGroup != null && !trigger.JobGroup.Equals(jobDetail.Group)) { throw new SchedulerException("Trigger does not reference given job!", SchedulerException.ErrorClientError); } trigger.Validate(); ICalendar cal = null; if (trigger.CalendarName != null) { cal = resources.JobStore.RetrieveCalendar(ctxt, trigger.CalendarName); if (cal == null) { throw new SchedulerException(string.Format(CultureInfo.InvariantCulture, "Calendar not found: {0}", trigger.CalendarName), SchedulerException.ErrorPersistenceCalendarDoesNotExist); } } NullableDateTime ft = trigger.ComputeFirstFireTimeUtc(cal); if (!ft.HasValue) { throw new SchedulerException("Based on configured schedule, the given trigger will never fire.", SchedulerException.ErrorClientError); } resources.JobStore.StoreJobAndTrigger(ctxt, jobDetail, trigger); NotifySchedulerThread(trigger.GetNextFireTimeUtc()); NotifySchedulerListenersScheduled(trigger); return ft.Value; }
private void DoUpdateOfMisfiredTrigger(ConnectionAndTransactionHolder conn, SchedulingContext ctxt, Trigger trig, bool forceState, string newStateIfNotComplete, bool recovering) { ICalendar cal = null; if (trig.CalendarName != null) { cal = RetrieveCalendar(conn, ctxt, trig.CalendarName); } signaler.NotifyTriggerListenersMisfired(trig); trig.UpdateAfterMisfire(cal); if (!trig.GetNextFireTimeUtc().HasValue) { StoreTrigger(conn, ctxt, trig, null, true, StateComplete, forceState, recovering); } else { StoreTrigger(conn, ctxt, trig, null, true, newStateIfNotComplete, forceState, false); } }
protected virtual void TriggeredJobComplete(ConnectionAndTransactionHolder conn, SchedulingContext ctxt, Trigger trigger, JobDetail jobDetail, SchedulerInstruction triggerInstCode) { try { if (triggerInstCode == SchedulerInstruction.DeleteTrigger) { if (!trigger.GetNextFireTimeUtc().HasValue) { // double check for possible reschedule within job // execution, which would cancel the need to delete... TriggerStatus stat = Delegate.SelectTriggerStatus(conn, trigger.Name, trigger.Group); if (stat != null && !stat.NextFireTimeUtc.HasValue) { RemoveTrigger(conn, ctxt, trigger.Name, trigger.Group); } } else { RemoveTrigger(conn, ctxt, trigger.Name, trigger.Group); signaler.SignalSchedulingChange(null); } } else if (triggerInstCode == SchedulerInstruction.SetTriggerComplete) { Delegate.UpdateTriggerState(conn, trigger.Name, trigger.Group, StateComplete); signaler.SignalSchedulingChange(null); } else if (triggerInstCode == SchedulerInstruction.SetTriggerError) { Log.Info("Trigger " + trigger.FullName + " set to ERROR state."); Delegate.UpdateTriggerState(conn, trigger.Name, trigger.Group, StateError); signaler.SignalSchedulingChange(null); } else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersComplete) { Delegate.UpdateTriggerStatesForJob(conn, trigger.JobName, trigger.JobGroup, StateComplete); signaler.SignalSchedulingChange(null); } else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersError) { Log.Info("All triggers of Job " + trigger.FullJobName + " set to ERROR state."); Delegate.UpdateTriggerStatesForJob(conn, trigger.JobName, trigger.JobGroup, StateError); signaler.SignalSchedulingChange(null); } if (jobDetail.Stateful) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jobDetail.Name, jobDetail.Group, StateWaiting, StateBlocked); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jobDetail.Name, jobDetail.Group, StatePaused, StatePausedBlocked); signaler.SignalSchedulingChange(null); try { if (jobDetail.JobDataMap.Dirty) { Delegate.UpdateJobData(conn, jobDetail); } } catch (IOException e) { throw new JobPersistenceException("Couldn't serialize job data: " + e.Message, e); } catch (Exception e) { throw new JobPersistenceException("Couldn't update job data: " + e.Message, e); } } } catch (Exception e) { throw new JobPersistenceException("Couldn't update trigger state(s): " + e.Message, e); } try { Delegate.DeleteFiredTrigger(conn, trigger.FireInstanceId); } catch (Exception e) { throw new JobPersistenceException("Couldn't delete fired trigger: " + e.Message, e); } }
protected virtual TriggerFiredBundle TriggerFired(ConnectionAndTransactionHolder conn, SchedulingContext ctxt, Trigger trigger) { JobDetail job; ICalendar cal = null; // Make sure trigger wasn't deleted, paused, or completed... try { // if trigger was deleted, state will be StateDeleted String state = Delegate.SelectTriggerState(conn, trigger.Name, trigger.Group); if (!state.Equals(StateAcquired)) { return null; } } catch (Exception e) { throw new JobPersistenceException("Couldn't select trigger state: " + e.Message, e); } try { job = RetrieveJob(conn, ctxt, trigger.JobName, trigger.JobGroup); if (job == null) { return null; } } catch (JobPersistenceException jpe) { try { Log.Error("Error retrieving job, setting trigger state to ERROR.", jpe); Delegate.UpdateTriggerState(conn, trigger.Name, trigger.Group, StateError); } catch (Exception sqle) { Log.Error("Unable to set trigger state to ERROR.", sqle); } throw; } if (trigger.CalendarName != null) { cal = RetrieveCalendar(conn, ctxt, trigger.CalendarName); if (cal == null) { return null; } } try { Delegate.DeleteFiredTrigger(conn, trigger.FireInstanceId); Delegate.InsertFiredTrigger(conn, trigger, StateExecuting, job); } catch (Exception e) { throw new JobPersistenceException("Couldn't insert fired trigger: " + e.Message, e); } NullableDateTime prevFireTime = trigger.GetPreviousFireTimeUtc(); // call triggered - to update the trigger's next-fire-time state... trigger.Triggered(cal); String state2 = StateWaiting; bool force = true; if (job.Stateful) { state2 = StateBlocked; force = false; try { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, job.Name, job.Group, StateBlocked, StateWaiting); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, job.Name, job.Group, StateBlocked, StateAcquired); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, job.Name, job.Group, StatePausedBlocked, StatePaused); } catch (Exception e) { throw new JobPersistenceException("Couldn't update states of blocked triggers: " + e.Message, e); } } if (!trigger.GetNextFireTimeUtc().HasValue) { state2 = StateComplete; force = true; } StoreTrigger(conn, ctxt, trigger, job, true, state2, force, false); job.JobDataMap.ClearDirtyFlag(); return new TriggerFiredBundle( job, trigger, cal, trigger.Group.Equals(SchedulerConstants.DefaultRecoveryGroup), DateTime.UtcNow, trigger.GetPreviousFireTimeUtc(), prevFireTime, trigger.GetNextFireTimeUtc()); }
private static DateTime? GetNextRunDate(Trigger trigger) { var nextFire = trigger.GetNextFireTimeUtc(); return nextFire == null ? (DateTime?) null : nextFire.Value.ToLocalTime(); }
//--------------------------------------------------------------------------- // triggers //--------------------------------------------------------------------------- /// <summary> /// Insert the base trigger data. /// </summary> /// <param name="conn">the DB Connection</param> /// <param name="trigger">the trigger to insert</param> /// <param name="state">the state that the trigger should be stored in</param> /// <param name="jobDetail">The job detail.</param> /// <returns>the number of rows inserted</returns> public virtual int InsertTrigger(ConnectionAndTransactionHolder conn, Trigger trigger, string state, JobDetail jobDetail) { byte[] baos = null; if (trigger.JobDataMap.Count > 0) { baos = SerializeJobData(trigger.JobDataMap); } using (IDbCommand cmd = PrepareCommand(conn, ReplaceTablePrefix(SqlInsertTrigger))) { AddCommandParameter(cmd, 1, "triggerName", trigger.Name); AddCommandParameter(cmd, 2, "triggerGroup", trigger.Group); AddCommandParameter(cmd, 3, "triggerJobName", trigger.JobName); AddCommandParameter(cmd, 4, "triggerJobGroup", trigger.JobGroup); AddCommandParameter(cmd, 5, "triggerVolatile", GetDbBooleanValue(trigger.Volatile)); AddCommandParameter(cmd, 6, "triggerDescription", trigger.Description); if (trigger.GetNextFireTimeUtc().HasValue) { AddCommandParameter(cmd, 7, "triggerNextFireTime", Convert.ToDecimal(trigger.GetNextFireTimeUtc().Value.Ticks)); } else { AddCommandParameter(cmd, 7, "triggerNextFireTime", null); } long prevFireTime = -1; if (trigger.GetPreviousFireTimeUtc().HasValue) { prevFireTime = trigger.GetPreviousFireTimeUtc().Value.Ticks; } AddCommandParameter(cmd, 8, "triggerPreviousFireTime", Convert.ToDecimal(prevFireTime)); AddCommandParameter(cmd, 9, "triggerState", state); string paramName = "triggerType"; if (trigger is SimpleTrigger && !trigger.HasAdditionalProperties) { AddCommandParameter(cmd, 10, paramName, TriggerTypeSimple); } else if (trigger is CronTrigger && !trigger.HasAdditionalProperties) { AddCommandParameter(cmd, 10, paramName, TriggerTypeCron); } else { // (trigger instanceof BlobTrigger or additional properties in sub-class AddCommandParameter(cmd, 10, paramName, TriggerTypeBlob); } AddCommandParameter(cmd, 11, "triggerStartTime", Convert.ToDecimal(trigger.StartTimeUtc.Ticks)); long endTime = 0; if (trigger.EndTimeUtc.HasValue) { endTime = trigger.EndTimeUtc.Value.Ticks; } AddCommandParameter(cmd, 12, "triggerEndTime", Convert.ToDecimal(endTime)); AddCommandParameter(cmd, 13, "triggerCalendarName", trigger.CalendarName); AddCommandParameter(cmd, 14, "triggerMisfireInstruction", trigger.MisfireInstruction); paramName = "triggerJobJobDataMap"; if (baos != null) { AddCommandParameter(cmd, 15, paramName, baos, dbProvider.Metadata.DbBinaryType); } else { AddCommandParameter(cmd, 15, paramName, null, dbProvider.Metadata.DbBinaryType); } AddCommandParameter(cmd, 16, "triggerPriority", trigger.Priority); int insertResult = cmd.ExecuteNonQuery(); if (insertResult > 0) { string[] trigListeners = trigger.TriggerListenerNames; for (int i = 0; trigListeners != null && i < trigListeners.Length; i++) { InsertTriggerListener(conn, trigger, trigListeners[i]); } } return insertResult; } }
/// <summary> /// Insert a fired trigger. /// </summary> /// <param name="conn">the DB Connection</param> /// <param name="trigger">the trigger</param> /// <param name="state">the state that the trigger should be stored in</param> /// <param name="job">The job.</param> /// <returns>the number of rows inserted</returns> public virtual int InsertFiredTrigger(ConnectionAndTransactionHolder conn, Trigger trigger, string state, JobDetail job) { using (IDbCommand cmd = PrepareCommand(conn, ReplaceTablePrefix(SqlInsertFiredTrigger))) { AddCommandParameter(cmd, 1, "triggerEntryId", trigger.FireInstanceId); AddCommandParameter(cmd, 2, "triggerName", trigger.Name); AddCommandParameter(cmd, 3, "triggerGroup", trigger.Group); AddCommandParameter(cmd, 4, "triggerVolatile", GetDbBooleanValue(trigger.Volatile)); AddCommandParameter(cmd, 5, "triggerInstanceName", instanceId); AddCommandParameter(cmd, 6, "triggerFireTime", Convert.ToDecimal(trigger.GetNextFireTimeUtc().Value.Ticks)); AddCommandParameter(cmd, 7, "triggerState", state); if (job != null) { AddCommandParameter(cmd, 8, "triggerJobName", trigger.JobName); AddCommandParameter(cmd, 9, "triggerJobGroup", trigger.JobGroup); AddCommandParameter(cmd, 10, "triggerJobStateful", GetDbBooleanValue(job.Stateful)); AddCommandParameter(cmd, 11, "triggerJobRequestsRecovery", GetDbBooleanValue(job.RequestsRecovery)); } else { AddCommandParameter(cmd, 8, "triggerJobName", null); AddCommandParameter(cmd, 9, "triggerJobGroup", null); AddCommandParameter(cmd, 10, "triggerJobStateful", GetDbBooleanValue(false)); AddCommandParameter(cmd, 11, "triggerJobRequestsRecovery", GetDbBooleanValue(false)); } AddCommandParameter(cmd, 12, "triggerPriority", trigger.Priority); return cmd.ExecuteNonQuery(); } }
/// <summary> /// Update the base trigger data. /// </summary> /// <param name="conn">The DB Connection.</param> /// <param name="trigger">The trigger to insert.</param> /// <param name="state">The state that the trigger should be stored in.</param> /// <param name="jobDetail">The job detail.</param> /// <returns>The number of rows updated.</returns> public virtual int UpdateTrigger(ConnectionAndTransactionHolder conn, Trigger trigger, string state, JobDetail jobDetail) { // save some clock cycles by unnecessarily writing job data blob ... bool updateJobData = trigger.JobDataMap.Dirty; byte[] baos = null; if (updateJobData && trigger.JobDataMap.Count > 0) { baos = SerializeJobData(trigger.JobDataMap); } IDbCommand cmd; int insertResult; if (updateJobData) { cmd = PrepareCommand(conn, ReplaceTablePrefix(SqlUpdateTrigger)); } else { cmd = PrepareCommand(conn, ReplaceTablePrefix(SqlUpdateTriggerSkipData)); } AddCommandParameter(cmd, 1, "triggerJobName", trigger.JobName); AddCommandParameter(cmd, 2, "triggerJobGroup", trigger.JobGroup); AddCommandParameter(cmd, 3, "triggerVolatile", GetDbBooleanValue(trigger.Volatile)); AddCommandParameter(cmd, 4, "triggerDescription", trigger.Description); long nextFireTime = -1; if (trigger.GetNextFireTimeUtc().HasValue) { nextFireTime = trigger.GetNextFireTimeUtc().Value.Ticks; } AddCommandParameter(cmd, 5, "triggerNextFireTime", Convert.ToDecimal(nextFireTime)); long prevFireTime = -1; if (trigger.GetPreviousFireTimeUtc().HasValue) { prevFireTime = trigger.GetPreviousFireTimeUtc().Value.Ticks; } AddCommandParameter(cmd, 6, "triggerPreviousFireTime", Convert.ToDecimal(prevFireTime)); AddCommandParameter(cmd, 7, "triggerState", state); string paramName = "triggerType"; if (trigger is SimpleTrigger && !trigger.HasAdditionalProperties) { // UpdateSimpleTrigger(conn, (SimpleTrigger)trigger); AddCommandParameter(cmd, 8, paramName, TriggerTypeSimple); } else if (trigger is CronTrigger && !trigger.HasAdditionalProperties) { // UpdateCronTrigger(conn, (CronTrigger)trigger); AddCommandParameter(cmd, 8, paramName, TriggerTypeCron); } else { // UpdateBlobTrigger(conn, trigger); AddCommandParameter(cmd, 8, paramName, TriggerTypeBlob); } AddCommandParameter(cmd, 9, "triggerStartTime", Convert.ToDecimal(trigger.StartTimeUtc.Ticks)); long endTime = 0; if (trigger.EndTimeUtc.HasValue) { endTime = trigger.EndTimeUtc.Value.Ticks; } AddCommandParameter(cmd, 10, "triggerEndTime", Convert.ToDecimal(endTime)); AddCommandParameter(cmd, 11, "triggerCalendarName", trigger.CalendarName); AddCommandParameter(cmd, 12, "triggerMisfireInstruction", trigger.MisfireInstruction); AddCommandParameter(cmd, 13, "triggerPriority", trigger.Priority); paramName = "triggerJobJobDataMap"; if (updateJobData) { if (baos != null) { AddCommandParameter(cmd, 14, paramName, baos, dbProvider.Metadata.DbBinaryType); } else { AddCommandParameter(cmd, 14, paramName, null, dbProvider.Metadata.DbBinaryType); } AddCommandParameter(cmd, 15, "triggerName", trigger.Name); AddCommandParameter(cmd, 16, "triggerGroup", trigger.Group); } else { AddCommandParameter(cmd, 14, "triggerName", trigger.Name); AddCommandParameter(cmd, 15, "triggerGroup", trigger.Group); } insertResult = cmd.ExecuteNonQuery(); if (insertResult > 0) { DeleteTriggerListeners(conn, trigger.Name, trigger.Group); String[] trigListeners = trigger.TriggerListenerNames; for (int i = 0; trigListeners != null && i < trigListeners.Length; i++) { InsertTriggerListener(conn, trigger, trigListeners[i]); } } return insertResult; }