/// <summary> /// Returns a list of Dates that are the next fire times of a /// <see cref="ITrigger" />. /// 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<DateTimeOffset> ComputeFireTimes(IOperableTrigger trigg, ICalendar cal, int numTimes) { List<DateTimeOffset> lst = new List<DateTimeOffset>(); IOperableTrigger t = (IOperableTrigger) trigg.Clone(); if (t.GetNextFireTimeUtc() == null || !t.GetNextFireTimeUtc().HasValue) { t.ComputeFirstFireTimeUtc(cal); } for (int i = 0; i < numTimes; i++) { DateTimeOffset? d = t.GetNextFireTimeUtc(); if (d.HasValue) { lst.Add(d.Value); t.Triggered(cal); } else { break; } } return lst.AsReadOnly(); }
public void Initialize() { _job = CreateJob(); _trigger1 = CreateTrigger("trigger1", "triggerGroup1", _job.Key); _trigger2 = CreateTrigger("trigger2", "triggerGroup1", _job.Key); JobStore.StoreJob(_job, false); JobStore.StoreTrigger(_trigger1, false); JobStore.StoreTrigger(_trigger2, false); }
protected override SimplePropertiesTriggerProperties GetTriggerProperties(IOperableTrigger trigger) { CalendarIntervalTriggerImpl calTrig = (CalendarIntervalTriggerImpl) trigger; SimplePropertiesTriggerProperties props = new SimplePropertiesTriggerProperties(); props.Int1 = (calTrig.RepeatInterval); props.String1 = (calTrig.RepeatIntervalUnit.ToString()); props.Int2 = (calTrig.TimesTriggered); return props; }
public int InsertExtendedTriggerProperties(ConnectionAndTransactionHolder conn, IOperableTrigger trigger, string state, IJobDetail jobDetail) { ICronTrigger cronTrigger = (ICronTrigger) trigger; using (IDbCommand cmd = DbAccessor.PrepareCommand(conn, AdoJobStoreUtil.ReplaceTablePrefix(StdAdoConstants.SqlInsertCronTrigger, TablePrefix, SchedNameLiteral))) { DbAccessor.AddCommandParameter(cmd, "triggerName", trigger.Key.Name); DbAccessor.AddCommandParameter(cmd, "triggerGroup", trigger.Key.Group); DbAccessor.AddCommandParameter(cmd, "triggerCronExpression", cronTrigger.CronExpressionString); DbAccessor.AddCommandParameter(cmd, "triggerTimeZone", cronTrigger.TimeZone.Id); return cmd.ExecuteNonQuery(); } }
public int InsertExtendedTriggerProperties(ConnectionAndTransactionHolder conn, IOperableTrigger trigger, string state, IJobDetail jobDetail) { ISimpleTrigger simpleTrigger = (ISimpleTrigger) trigger; using (IDbCommand cmd = commandAccessor.PrepareCommand(conn, AdoJobStoreUtil.ReplaceTablePrefix(StdAdoConstants.SqlInsertSimpleTrigger, tablePrefix, schedNameLiteral))) { commandAccessor.AddCommandParameter(cmd, "triggerName", trigger.Key.Name); commandAccessor.AddCommandParameter(cmd, "triggerGroup", trigger.Key.Group); commandAccessor.AddCommandParameter(cmd, "triggerRepeatCount", simpleTrigger.RepeatCount); commandAccessor.AddCommandParameter(cmd, "triggerRepeatInterval", simpleTrigger.RepeatInterval.TotalMilliseconds); commandAccessor.AddCommandParameter(cmd, "triggerTimesTriggered", simpleTrigger.TimesTriggered); return cmd.ExecuteNonQuery(); } }
protected override SimplePropertiesTriggerProperties GetTriggerProperties(IOperableTrigger trigger) { CalendarIntervalTriggerImpl calTrig = (CalendarIntervalTriggerImpl) trigger; SimplePropertiesTriggerProperties props = new SimplePropertiesTriggerProperties(); props.Int1 = calTrig.RepeatInterval; props.String1 = calTrig.RepeatIntervalUnit.ToString(); props.Int2 = calTrig.TimesTriggered; props.String2 = calTrig.TimeZone.Id; props.Boolean1 = calTrig.PreserveHourOfDayAcrossDaylightSavings; props.Boolean2 = calTrig.SkipDayIfHourDoesNotExist; return props; }
/// <summary> /// Initializes a new instance of the <see cref="TriggerFiredBundle"/> class. /// </summary> /// <param name="job">The job.</param> /// <param name="trigger">The trigger.</param> /// <param name="cal">The calendar.</param> /// <param name="jobIsRecovering">if set to <c>true</c> [job is recovering].</param> /// <param name="fireTimeUtc">The fire time.</param> /// <param name="scheduledFireTimeUtc">The scheduled fire time.</param> /// <param name="prevFireTimeUtc">The previous fire time.</param> /// <param name="nextFireTimeUtc">The next fire time.</param> public TriggerFiredBundle(IJobDetail job, IOperableTrigger trigger, ICalendar cal, bool jobIsRecovering, DateTimeOffset? fireTimeUtc, DateTimeOffset? scheduledFireTimeUtc, DateTimeOffset? prevFireTimeUtc, DateTimeOffset? nextFireTimeUtc) { this.job = job; this.trigger = trigger; this.cal = cal; this.jobIsRecovering = jobIsRecovering; this.fireTimeUtc = fireTimeUtc; this.scheduledFireTimeUtc = scheduledFireTimeUtc; this.prevFireTimeUtc = prevFireTimeUtc; this.nextFireTimeUtc = nextFireTimeUtc; }
public async Task TestAcquireTriggers() { ISchedulerSignaler schedSignaler = new SampleSignaler(); ITypeLoadHelper loadHelper = new SimpleTypeLoadHelper(); loadHelper.Initialize(); RAMJobStore store = new RAMJobStore(); await store.Initialize(loadHelper, schedSignaler); // Setup: Store jobs and triggers. DateTime startTime0 = DateTime.UtcNow.AddMinutes(1).ToUniversalTime(); // a min from now. for (int i = 0; i < 10; i++) { DateTime startTime = startTime0.AddMinutes(i * 1); // a min apart IJobDetail job = JobBuilder.Create <NoOpJob>().WithIdentity("job" + i).Build(); SimpleScheduleBuilder schedule = SimpleScheduleBuilder.RepeatMinutelyForever(2); IOperableTrigger trigger = (IOperableTrigger)TriggerBuilder.Create().WithIdentity("job" + i).WithSchedule(schedule).ForJob(job).StartAt(startTime).Build(); // Manually trigger the first fire time computation that scheduler would do. Otherwise // the store.acquireNextTriggers() will not work properly. DateTimeOffset?fireTime = trigger.ComputeFirstFireTimeUtc(null); Assert.AreEqual(true, fireTime != null); await store.StoreJobAndTrigger(job, trigger); } // Test acquire one trigger at a time for (int i = 0; i < 10; i++) { DateTimeOffset noLaterThan = startTime0.AddMinutes(i); int maxCount = 1; TimeSpan timeWindow = TimeSpan.Zero; var triggers = await store.AcquireNextTriggers(noLaterThan, maxCount, timeWindow); Assert.AreEqual(1, triggers.Count); Assert.AreEqual("job" + i, triggers[0].Key.Name); // Let's remove the trigger now. await store.RemoveJob(triggers[0].JobKey); } }
/// <summary> /// Releases the trigger retry loop. /// </summary> /// <param name="trigger">The trigger.</param> public virtual void ReleaseTriggerRetryLoop(IOperableTrigger trigger) { int retryCount = 0; try { while (!halted) { try { Thread.Sleep(DbFailureRetryInterval); // retry every N seconds (the db connection must be failed) retryCount++; qsRsrcs.JobStore.ReleaseAcquiredTrigger(trigger); retryCount = 0; break; } catch (JobPersistenceException jpe) { if (retryCount % 4 == 0) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "An error occurred while releasing trigger '{0}'", trigger.Key), jpe); } } catch (ThreadInterruptedException e) { Log.Error(string.Format(CultureInfo.InvariantCulture, "ReleaseTriggerRetryLoop: InterruptedException {0}", e.Message), e); } catch (Exception e) { Log.Error(string.Format(CultureInfo.InvariantCulture, "ReleaseTriggerRetryLoop: Exception {0}", e.Message), e); } } } finally { if (retryCount == 0) { Log.Info("ReleaseTriggerRetryLoop: connection restored."); } } }
/// <summary> /// Initializes a new instance of the <see cref="TriggerFiredBundle"/> class. /// </summary> /// <param name="job">The job.</param> /// <param name="trigger">The trigger.</param> /// <param name="cal">The calendar.</param> /// <param name="jobIsRecovering">if set to <c>true</c> [job is recovering].</param> /// <param name="fireTimeUtc">The fire time.</param> /// <param name="scheduledFireTimeUtc">The scheduled fire time.</param> /// <param name="prevFireTimeUtc">The previous fire time.</param> /// <param name="nextFireTimeUtc">The next fire time.</param> public TriggerFiredBundle( IJobDetail job, IOperableTrigger trigger, ICalendar cal, bool jobIsRecovering, DateTimeOffset fireTimeUtc, DateTimeOffset?scheduledFireTimeUtc, DateTimeOffset?prevFireTimeUtc, DateTimeOffset?nextFireTimeUtc) { JobDetail = job; Trigger = trigger; Calendar = cal; Recovering = jobIsRecovering; FireTimeUtc = fireTimeUtc; ScheduledFireTimeUtc = scheduledFireTimeUtc; PrevFireTimeUtc = prevFireTimeUtc; NextFireTimeUtc = nextFireTimeUtc; }
public async Task <int> InsertExtendedTriggerProperties( ConnectionAndTransactionHolder conn, IOperableTrigger trigger, string state, IJobDetail jobDetail, CancellationToken cancellationToken = default) { ISimpleTrigger simpleTrigger = (ISimpleTrigger)trigger; using (var cmd = DbAccessor.PrepareCommand(conn, AdoJobStoreUtil.ReplaceTablePrefix(StdAdoConstants.SqlInsertSimpleTrigger, TablePrefix, SchedNameLiteral))) { DbAccessor.AddCommandParameter(cmd, "triggerName", trigger.Key.Name); DbAccessor.AddCommandParameter(cmd, "triggerGroup", trigger.Key.Group); DbAccessor.AddCommandParameter(cmd, "triggerRepeatCount", simpleTrigger.RepeatCount); DbAccessor.AddCommandParameter(cmd, "triggerRepeatInterval", DbAccessor.GetDbTimeSpanValue(simpleTrigger.RepeatInterval)); DbAccessor.AddCommandParameter(cmd, "triggerTimesTriggered", simpleTrigger.TimesTriggered); return(await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false)); } }
public async Task <int> InsertExtendedTriggerProperties( ConnectionAndTransactionHolder conn, IOperableTrigger trigger, string state, IJobDetail jobDetail, CancellationToken cancellationToken = default) { ICronTrigger cronTrigger = (ICronTrigger)trigger; using (var cmd = DbAccessor.PrepareCommand(conn, AdoJobStoreUtil.ReplaceTablePrefix(StdAdoConstants.SqlInsertCronTrigger, TablePrefix))) { DbAccessor.AddCommandParameter(cmd, "schedulerName", SchedName); DbAccessor.AddCommandParameter(cmd, "triggerName", trigger.Key.Name); DbAccessor.AddCommandParameter(cmd, "triggerGroup", trigger.Key.Group); DbAccessor.AddCommandParameter(cmd, "triggerCronExpression", cronTrigger.CronExpressionString); DbAccessor.AddCommandParameter(cmd, "triggerTimeZone", cronTrigger.TimeZone.Id); return(await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false)); } }
public void TestAcquireTriggersInBatch() { ISchedulerSignaler schedSignaler = new SampleSignaler(); ITypeLoadHelper loadHelper = new SimpleTypeLoadHelper(); loadHelper.Initialize(); var store = new RavenJobStore(); store.Initialize(loadHelper, schedSignaler); // Setup: Store jobs and triggers. DateTimeOffset startTime0 = DateTimeOffset.UtcNow.AddMinutes(1); // a min from now. for (int i = 0; i < 10; i++) { DateTimeOffset startTime = startTime0.AddMinutes(i); // a min apart IJobDetail job = JobBuilder.Create <NoOpJob>().WithIdentity("job" + i).Build(); SimpleScheduleBuilder schedule = SimpleScheduleBuilder.RepeatMinutelyForever(2); IOperableTrigger trigger = (IOperableTrigger)TriggerBuilder.Create().WithIdentity("trigger" + i).WithSchedule(schedule).ForJob(job).StartAt(startTime).Build(); // Manually trigger the first fire time computation that scheduler would do. Otherwise // the store.acquireNextTriggers() will not work properly. DateTimeOffset?fireTime = trigger.ComputeFirstFireTimeUtc(null); Assert.AreEqual(true, fireTime != null); store.StoreJobAndTrigger(job, trigger); } // Test acquire batch of triggers at a time DateTimeOffset noLaterThan = startTime0.AddMinutes(10); int maxCount = 7; TimeSpan timeWindow = TimeSpan.FromMinutes(8); IList <IOperableTrigger> triggers = store.AcquireNextTriggers(noLaterThan, maxCount, timeWindow); Assert.AreEqual(7, triggers.Count); for (int i = 0; i < 7; i++) { Assert.AreEqual("trigger" + i, triggers[i].Key.Name); } }
protected override SimplePropertiesTriggerProperties GetTriggerProperties(IOperableTrigger trigger) { DailyTimeIntervalTriggerImpl dailyTrigger = (DailyTimeIntervalTriggerImpl) trigger; SimplePropertiesTriggerProperties props = new SimplePropertiesTriggerProperties(); props.Int1 = dailyTrigger.RepeatInterval; props.String1 = dailyTrigger.RepeatIntervalUnit.ToString(); props.Int2 = dailyTrigger.TimesTriggered; ISet<DayOfWeek> days = dailyTrigger.DaysOfWeek; string daysStr = string.Join(",", days.Cast<int>().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray()); props.String2 = daysStr; StringBuilder timeOfDayBuffer = new StringBuilder(); TimeOfDay startTimeOfDay = dailyTrigger.StartTimeOfDay; if (startTimeOfDay != null) { timeOfDayBuffer.Append(startTimeOfDay.Hour).Append(","); timeOfDayBuffer.Append(startTimeOfDay.Minute).Append(","); timeOfDayBuffer.Append(startTimeOfDay.Second).Append(","); } else { timeOfDayBuffer.Append(",,,"); } TimeOfDay endTimeOfDay = dailyTrigger.EndTimeOfDay; if (endTimeOfDay != null) { timeOfDayBuffer.Append(endTimeOfDay.Hour).Append(","); timeOfDayBuffer.Append(endTimeOfDay.Minute).Append(","); timeOfDayBuffer.Append(endTimeOfDay.Second); } else { timeOfDayBuffer.Append(",,,"); } props.String3 = timeOfDayBuffer.ToString(); props.Long1 = dailyTrigger.RepeatCount; return props; }
/// <summary> /// Vetoeds the job retry loop. /// </summary> /// <param name="trigger">The trigger.</param> /// <param name="jobDetail">The job detail.</param> /// <param name="instCode">The inst code.</param> /// <returns></returns> public bool VetoedJobRetryLoop(IOperableTrigger trigger, IJobDetail jobDetail, SchedulerInstruction instCode) { while (!shutdownRequested) { try { Thread.Sleep(qs.DbRetryInterval); // retry per config setting (the db connection must be failed) qs.NotifyJobStoreJobVetoed(trigger, jobDetail, instCode); return(true); } catch (JobPersistenceException jpe) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "An error occured while marking executed job vetoed. job= '{0}'", jobDetail.Key), jpe); } catch (ThreadInterruptedException) { } } return(false); }
/// <summary> /// Compute the <see cref="DateTimeOffset" /> that is 1 second after the Nth firing of /// the given <see cref="ITrigger" />, taking the trigger's associated /// <see cref="ICalendar" /> into consideration. /// </summary> /// <remarks> /// The input trigger will be cloned before any work is done, so you need /// not worry about its state being altered by this method. /// </remarks> /// <param name="trigger">The trigger upon which to do the work</param> /// <param name="calendar">The calendar to apply to the trigger's schedule</param> /// <param name="numberOfTimes">The number of next fire times to produce</param> /// <returns>the computed Date, or null if the trigger (as configured) will not fire that many times</returns> public static DateTimeOffset?ComputeEndTimeToAllowParticularNumberOfFirings(IOperableTrigger trigger, ICalendar?calendar, int numberOfTimes) { IOperableTrigger t = (IOperableTrigger)trigger.Clone(); if (t.GetNextFireTimeUtc() == null) { t.ComputeFirstFireTimeUtc(calendar); } int c = 0; DateTimeOffset?endTime = null; for (int i = 0; i < numberOfTimes; i++) { DateTimeOffset?d = t.GetNextFireTimeUtc(); if (d != null) { c++; t.Triggered(calendar); if (c == numberOfTimes) { endTime = d; } } else { break; } } if (endTime == null) { return(null); } endTime = endTime.Value.AddSeconds(1); return(endTime); }
/// <summary> /// Compute the <see cref="DateTimeOffset" /> that is 1 second after the Nth firing of /// the given <see cref="ITrigger" />, taking the triger's associated /// <see cref="ICalendar" /> into consideration. /// </summary> /// <remarks> /// The input trigger will be cloned before any work is done, so you need /// not worry about its state being altered by this method. /// </remarks> /// <param name="trigger">The trigger upon which to do the work</param> /// <param name="calendar">The calendar to apply to the trigger's schedule</param> /// <param name="numberOfTimes">The number of next fire times to produce</param> /// <returns>the computed Date, or null if the trigger (as configured) will not fire that many times</returns> public static DateTimeOffset? ComputeEndTimeToAllowParticularNumberOfFirings(IOperableTrigger trigger, ICalendar calendar, int numberOfTimes) { IOperableTrigger t = (IOperableTrigger) trigger.Clone(); if (t.GetNextFireTimeUtc() == null) { t.ComputeFirstFireTimeUtc(calendar); } int c = 0; DateTimeOffset? endTime = null; for (int i = 0; i < numberOfTimes; i++) { DateTimeOffset? d = t.GetNextFireTimeUtc(); if (d != null) { c++; t.Triggered(calendar); if (c == numberOfTimes) { endTime = d; } } else { break; } } if (endTime == null) { return null; } endTime = endTime.Value.AddSeconds(1); return endTime; }
/// <summary> /// Returns a list of Dates that are the next fire times of a <see cref="ITrigger" /> /// 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. /// <para> /// NOTE: if this is a trigger that has previously fired within the given /// date range, then firings which have already occurred will not be listed /// in the output List. /// </para> /// </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> public static IReadOnlyList <DateTimeOffset> ComputeFireTimesBetween(IOperableTrigger trigg, ICalendar?cal, DateTimeOffset from, DateTimeOffset to) { List <DateTimeOffset> lst = new List <DateTimeOffset>(); IOperableTrigger t = (IOperableTrigger)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) { DateTimeOffset?d = t.GetNextFireTimeUtc(); if (d.HasValue) { if (d.Value < from) { t.Triggered(cal); continue; } if (d.Value > to) { break; } lst.Add(d.Value); t.Triggered(cal); } else { break; } } return(lst.AsReadOnly()); }
// Adapted from https://github.com/quartznet/quartznet/blob/master/src/Quartz/TriggerUtils.cs public static int ComputeFireTimeCountBetween(IOperableTrigger trigg, ICalendar cal, DateTimeOffset from, DateTimeOffset to) { int count = 0; IOperableTrigger t = (IOperableTrigger)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) { DateTimeOffset?d = t.GetNextFireTimeUtc(); if (d.HasValue) { if (d.Value < from) { t.Triggered(cal); continue; } if (d.Value > to) { break; } count++; t.Triggered(cal); } else { break; } } return(count); }
static IOperableTrigger CreateTriggerCore(this IXpandJobTrigger jobTrigger, Type jobType) { IOperableTrigger trigger = null; if (jobTrigger is IXpandSimpleTrigger) { trigger = new SimpleTriggerImpl(jobTrigger.Name, jobType.FullName); } if (jobTrigger is IXpandCronTrigger) { trigger = new CronTriggerImpl(jobTrigger.Name, jobType.FullName); } if (jobTrigger is INthIncludedDayTrigger) { trigger = new NthIncludedDayTrigger(jobTrigger.Name, jobType.FullName); } if (trigger != null) { return(trigger); } throw new NotImplementedException(jobTrigger.GetType().FullName); }
public static void AssignQuartzTrigger(this IOperableTrigger jobTrigger, IXpandJobTrigger trigger, string jobName, Type type, string jobGroup) { jobTrigger.EndTimeUtc = trigger.EndTimeUtc; jobTrigger.Priority = (int)trigger.Priority; jobTrigger.CalendarName = GetCalendarName(trigger); jobTrigger.JobDataMap = new JobDataMap(); jobTrigger.StartTimeUtc = trigger.StartTimeUtc; jobTrigger.Description = trigger.Description; jobTrigger.JobKey = new JobKey(jobName, type.FullName); jobTrigger.Key = new TriggerKey(jobTrigger.Key.Name, GetGroup(jobName, type, jobGroup)); if (jobTrigger is SimpleTriggerImpl) { ((SimpleTriggerImpl)jobTrigger).AssignQuartzTrigger((IXpandSimpleTrigger)trigger); } else if (jobTrigger is CronTriggerImpl) { ((CronTriggerImpl)jobTrigger).AssignQuartzTrigger((IXpandCronTrigger)trigger); } else if (jobTrigger is NthIncludedDayTrigger) { ((NthIncludedDayTrigger)jobTrigger).AssignQuartzTrigger((INthIncludedDayTrigger)trigger); } }
[Platform(Exclude = "Linux")] // TODO seems that we have some trouble on Linux with this public async Task ReschedulingTriggerShouldKeepOriginalNextFireTime() { NameValueCollection properties = new NameValueCollection(); properties["quartz.serializer.type"] = TestConstants.DefaultSerializerType; ISchedulerFactory factory = new StdSchedulerFactory(properties); IScheduler scheduler = await factory.GetScheduler(); await scheduler.Start(); var job = JobBuilder.Create <NoOpJob>().Build(); IOperableTrigger trigger = (IOperableTrigger)TriggerBuilder.Create() .WithSimpleSchedule(x => x.WithIntervalInHours(1).RepeatForever()) .ForJob(job) .StartNow() .Build(); await scheduler.ScheduleJob(job, trigger); trigger = (IOperableTrigger)await scheduler.GetTrigger(trigger.Key); Assert.That(trigger.GetPreviousFireTimeUtc(), Is.EqualTo(null)); var previousFireTimeUtc = DateTimeOffset.UtcNow.AddDays(1); trigger.SetPreviousFireTimeUtc(previousFireTimeUtc); trigger.SetNextFireTimeUtc(trigger.GetFireTimeAfter(previousFireTimeUtc)); await scheduler.RescheduleJob(trigger.Key, trigger); trigger = (IOperableTrigger)await scheduler.GetTrigger(trigger.Key); Assert.That(trigger.GetNextFireTimeUtc().Value.UtcDateTime, Is.EqualTo(previousFireTimeUtc.AddHours(1).UtcDateTime).Within(TimeSpan.FromSeconds(5))); await scheduler.Shutdown(true); }
protected virtual bool ReplaceTrigger(ConnectionAndTransactionHolder conn, TriggerKey triggerKey, IOperableTrigger newTrigger) { try { // this must be called before we delete the trigger, obviously IJobDetail job = Delegate.SelectJobForTrigger(conn, triggerKey, TypeLoadHelper); if (job == null) { return false; } if (!newTrigger.JobKey.Equals(job.Key)) { throw new JobPersistenceException("New trigger is not related to the same job as the old trigger."); } bool removedTrigger = DeleteTriggerAndChildren(conn, triggerKey); StoreTrigger(conn, newTrigger, job, false, StateWaiting, false, false); return removedTrigger; } catch (Exception e) { throw new JobPersistenceException("Couldn't remove trigger: " + e.Message, e); } }
public void TriggeredJobComplete(IOperableTrigger trigger, IJobDetail jobDetail, SchedulerInstruction triggerInstCode) { throw new NotImplementedException(); }
public bool CanHandleTriggerType(IOperableTrigger trigger) { return((trigger is CronTriggerImpl) && !((CronTriggerImpl)trigger).HasAdditionalProperties); }
/// <summary> /// Returns a list of Dates that are the next fire times of a <see cref="ITrigger" /> /// 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. /// <para> /// NOTE: if this is a trigger that has previously fired within the given /// date range, then firings which have already occurred will not be listed /// in the output List. /// </para> /// </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<DateTimeOffset> ComputeFireTimesBetween(IOperableTrigger trigg, ICalendar cal, DateTimeOffset from, DateTimeOffset to) { List<DateTimeOffset> lst = new List<DateTimeOffset>(); IOperableTrigger t = (IOperableTrigger) 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) { DateTimeOffset? d = t.GetNextFireTimeUtc(); if (d.HasValue) { if (d.Value < from) { t.Triggered(cal); continue; } if (d.Value > to) { break; } lst.Add(d.Value); t.Triggered(cal); } else { break; } } return lst.AsReadOnly(); }
public static DateTimeOffset? RescheduleJob(this IScheduler scheduler, IOperableTrigger trigger) { return scheduler.RescheduleJob(trigger.Key, trigger); }
protected virtual TriggerFiredBundle TriggerFired(ConnectionAndTransactionHolder conn, IOperableTrigger trigger) { IJobDetail 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.Key); if (!state.Equals(StateAcquired)) { return null; } } catch (Exception e) { throw new JobPersistenceException("Couldn't select trigger state: " + e.Message, e); } try { job = RetrieveJob(conn, trigger.JobKey); if (job == null) { return null; } } catch (JobPersistenceException jpe) { try { Log.Error("Error retrieving job, setting trigger state to ERROR.", jpe); Delegate.UpdateTriggerState(conn, trigger.Key, StateError); } catch (Exception sqle) { Log.Error("Unable to set trigger state to ERROR.", sqle); } throw; } if (trigger.CalendarName != null) { cal = RetrieveCalendar(conn, trigger.CalendarName); if (cal == null) { return null; } } try { Delegate.UpdateFiredTrigger(conn, trigger, StateExecuting, job); } catch (Exception e) { throw new JobPersistenceException("Couldn't insert fired trigger: " + e.Message, e); } DateTimeOffset? 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.ConcurrentExecutionDisallowed) { state2 = StateBlocked; force = false; try { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, job.Key, StateBlocked, StateWaiting); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, job.Key, StateBlocked, StateAcquired); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, job.Key, 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, trigger, job, true, state2, force, false); job.JobDataMap.ClearDirtyFlag(); return new TriggerFiredBundle( job, trigger, cal, trigger.Key.Group.Equals(SchedulerConstants.DefaultRecoveryGroup), SystemTime.UtcNow(), trigger.GetPreviousFireTimeUtc(), prevFireTime, trigger.GetNextFireTimeUtc()); }
/// <summary> /// Inform the <see cref="IJobStore" /> that the scheduler no longer plans to /// fire the given <see cref="ITrigger" />, that it had previously acquired /// (reserved). /// </summary> public virtual void ReleaseAcquiredTrigger(IOperableTrigger trigger) { lock (lockObject) { this.Triggers.Update( Query.EQ("_id", trigger.Key.ToBsonDocument()), Update.Unset("SchedulerInstanceId") .Set("State", "Waiting")); } }
public void StoreTrigger(IOperableTrigger newTrigger, bool replaceExisting) { throw new NotImplementedException(); }
public override bool CanHandleTriggerType(IOperableTrigger trigger) { var calendarIntervalTriggerImpl = trigger as CalendarIntervalTriggerImpl; return(calendarIntervalTriggerImpl != null && !calendarIntervalTriggerImpl.HasAdditionalProperties); }
public bool ReplaceTrigger(TriggerKey triggerKey, IOperableTrigger newTrigger) { throw new NotImplementedException(); }
public int InsertExtendedTriggerProperties(ConnectionAndTransactionHolder conn, IOperableTrigger trigger, string state, IJobDetail jobDetail) { ISimpleTrigger simpleTrigger = (ISimpleTrigger)trigger; using (IDbCommand cmd = commandAccessor.PrepareCommand(conn, AdoJobStoreUtil.ReplaceTablePrefix(StdAdoConstants.SqlInsertSimpleTrigger, tablePrefix, schedNameLiteral))) { commandAccessor.AddCommandParameter(cmd, "triggerName", trigger.Key.Name); commandAccessor.AddCommandParameter(cmd, "triggerGroup", trigger.Key.Group); commandAccessor.AddCommandParameter(cmd, "triggerRepeatCount", simpleTrigger.RepeatCount); commandAccessor.AddCommandParameter(cmd, "triggerRepeatInterval", simpleTrigger.RepeatInterval.TotalMilliseconds); commandAccessor.AddCommandParameter(cmd, "triggerTimesTriggered", simpleTrigger.TimesTriggered); return(cmd.ExecuteNonQuery()); } }
public void ReleaseAcquiredTrigger(IOperableTrigger trigger) { throw new NotImplementedException(); }
/// <summary> /// This method has to be implemented in order that starting of the thread causes the object's /// run method to be called in that separately executing thread. /// </summary> public virtual void Run() { qs.AddInternalSchedulerListener(this); try { IOperableTrigger trigger = (IOperableTrigger)jec.Trigger; IJobDetail jobDetail = jec.JobDetail; do { JobExecutionException jobExEx = null; IJob job = jec.JobInstance; try { Begin(); } catch (SchedulerException se) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "Error executing Job ({0}: couldn't begin execution.", jec.JobDetail.Key), se); break; } // notify job & trigger listeners... SchedulerInstruction instCode; try { if (!NotifyListenersBeginning(jec)) { break; } } catch (VetoedException) { try { instCode = trigger.ExecutionComplete(jec, null); try { qs.NotifyJobStoreJobVetoed(trigger, jobDetail, instCode); } catch (JobPersistenceException) { VetoedJobRetryLoop(trigger, jobDetail, instCode); } // Even if trigger got vetoed, we still needs to check to see if it's the trigger's finalized run or not. if (jec.Trigger.GetNextFireTimeUtc() == null) { qs.NotifySchedulerListenersFinalized(jec.Trigger); } Complete(true); } catch (SchedulerException se) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "Error during veto of Job ({0}: couldn't finalize execution.", jec.JobDetail.Key), se); } break; } DateTimeOffset startTime = SystemTime.UtcNow(); DateTimeOffset endTime; // Execute the job try { if (log.IsDebugEnabled) { log.Debug("Calling Execute on job " + jobDetail.Key); } job.Execute(jec); endTime = SystemTime.UtcNow(); } catch (JobExecutionException jee) { endTime = SystemTime.UtcNow(); jobExEx = jee; log.Info(string.Format(CultureInfo.InvariantCulture, "Job {0} threw a JobExecutionException: ", jobDetail.Key), jobExEx); } catch (Exception e) { endTime = SystemTime.UtcNow(); log.Error(string.Format(CultureInfo.InvariantCulture, "Job {0} threw an unhandled Exception: ", jobDetail.Key), e); SchedulerException se = new SchedulerException("Job threw an unhandled exception.", e); qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "Job ({0} threw an exception.", jec.JobDetail.Key), se); jobExEx = new JobExecutionException(se, false); } jec.JobRunTime = endTime - startTime; // notify all job listeners if (!NotifyJobListenersComplete(jec, jobExEx)) { break; } instCode = SchedulerInstruction.NoInstruction; // update the trigger try { instCode = trigger.ExecutionComplete(jec, jobExEx); if (log.IsDebugEnabled) { log.Debug(string.Format(CultureInfo.InvariantCulture, "Trigger instruction : {0}", instCode)); } } catch (Exception e) { // If this happens, there's a bug in the trigger... SchedulerException se = new SchedulerException("Trigger threw an unhandled exception.", e); qs.NotifySchedulerListenersError("Please report this error to the Quartz developers.", se); } // notify all trigger listeners if (!NotifyTriggerListenersComplete(jec, instCode)) { break; } // update job/trigger or re-Execute job if (instCode == SchedulerInstruction.ReExecuteJob) { if (log.IsDebugEnabled) { log.Debug("Rescheduling trigger to reexecute"); } jec.IncrementRefireCount(); try { Complete(false); } catch (SchedulerException se) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "Error executing Job ({0}: couldn't finalize execution.", jec.JobDetail.Key), se); } continue; } try { Complete(true); } catch (SchedulerException se) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "Error executing Job ({0}: couldn't finalize execution.", jec.JobDetail.Key), se); continue; } try { qs.NotifyJobStoreJobComplete(trigger, jobDetail, instCode); } catch (JobPersistenceException jpe) { qs.NotifySchedulerListenersError( string.Format(CultureInfo.InvariantCulture, "An error occured while marking executed job complete. job= '{0}'", jobDetail.Key), jpe); if (!CompleteTriggerRetryLoop(trigger, jobDetail, instCode)) { return; } } break; } while (true); } finally { qs.RemoveInternalSchedulerListener(this); if (jec != null && jec.JobInstance != null) { qs.JobFactory.ReturnJob(jec.JobInstance); } } }
/// <summary> /// Insert or update a trigger. /// </summary> protected virtual void StoreTrigger(ConnectionAndTransactionHolder conn, IOperableTrigger newTrigger, IJobDetail job, bool replaceExisting, string state, bool forceState, bool recovering) { bool existingTrigger = TriggerExists(conn, newTrigger.Key); if ((existingTrigger) && (!replaceExisting)) { throw new ObjectAlreadyExistsException(newTrigger); } try { if (!forceState) { bool shouldBepaused = Delegate.IsTriggerGroupPaused(conn, newTrigger.Key.Group); if (!shouldBepaused) { shouldBepaused = Delegate.IsTriggerGroupPaused(conn, AllGroupsPaused); if (shouldBepaused) { Delegate.InsertPausedTriggerGroup(conn, newTrigger.Key.Group); } } if (shouldBepaused && (state.Equals(StateWaiting) || state.Equals(StateAcquired))) { state = StatePaused; } } if (job == null) { job = Delegate.SelectJobDetail(conn, newTrigger.JobKey, TypeLoadHelper); } if (job == null) { throw new JobPersistenceException("The job (" + newTrigger.JobKey + ") referenced by the trigger does not exist."); } if (job.ConcurrentExecutionDisallowed && !recovering) { state = CheckBlockedState(conn, job.Key, state); } if (existingTrigger) { Delegate.UpdateTrigger(conn, newTrigger, state, job); } else { Delegate.InsertTrigger(conn, newTrigger, state, job); } } catch (Exception e) { string message = String.Format("Couldn't store trigger '{0}' for '{1}' job: {2}", newTrigger.Key, newTrigger.JobKey, e.Message); throw new JobPersistenceException(message, e); } }
protected virtual void TriggeredJobComplete(ConnectionAndTransactionHolder conn, IOperableTrigger trigger, IJobDetail 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.Key); if (stat != null && !stat.NextFireTimeUtc.HasValue) { RemoveTrigger(conn, trigger.Key); } } else { RemoveTrigger(conn, trigger.Key); SignalSchedulingChangeOnTxCompletion(null); } } else if (triggerInstCode == SchedulerInstruction.SetTriggerComplete) { Delegate.UpdateTriggerState(conn, trigger.Key, StateComplete); SignalSchedulingChangeOnTxCompletion(null); } else if (triggerInstCode == SchedulerInstruction.SetTriggerError) { Log.Info("Trigger " + trigger.Key + " set to ERROR state."); Delegate.UpdateTriggerState(conn, trigger.Key, StateError); SignalSchedulingChangeOnTxCompletion(null); } else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersComplete) { Delegate.UpdateTriggerStatesForJob(conn, trigger.JobKey, StateComplete); SignalSchedulingChangeOnTxCompletion(null); } else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersError) { Log.Info("All triggers of Job " + trigger.JobKey + " set to ERROR state."); Delegate.UpdateTriggerStatesForJob(conn, trigger.JobKey, StateError); SignalSchedulingChangeOnTxCompletion(null); } if (jobDetail.ConcurrentExecutionDisallowed) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jobDetail.Key, StateWaiting, StateBlocked); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jobDetail.Key, StatePaused, StatePausedBlocked); SignalSchedulingChangeOnTxCompletion(null); } if (jobDetail.PersistJobDataAfterExecution) { 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 internal void NotifyJobStoreJobVetoed(IOperableTrigger trigger, IJobDetail detail, SchedulerInstruction instCode) { resources.JobStore.TriggeredJobComplete(trigger, detail, instCode); }
private void DoUpdateOfMisfiredTrigger(ConnectionAndTransactionHolder conn, IOperableTrigger trig, bool forceState, string newStateIfNotComplete, bool recovering) { ICalendar cal = null; if (trig.CalendarName != null) { cal = RetrieveCalendar(conn, trig.CalendarName); } schedSignaler.NotifyTriggerListenersMisfired(trig); trig.UpdateAfterMisfire(cal); if (!trig.GetNextFireTimeUtc().HasValue) { StoreTrigger(conn, trig, null, true, StateComplete, forceState, recovering); schedSignaler.NotifySchedulerListenersFinalized(trig); } else { StoreTrigger(conn, trig, null, true, newStateIfNotComplete, forceState, false); } }
public override bool CanHandleTriggerType(IOperableTrigger trigger) { return((trigger is DailyTimeIntervalTriggerImpl) && !((DailyTimeIntervalTriggerImpl)trigger).HasAdditionalProperties); }
public static void StoreTrigger(this IScheduler scheduler, IOperableTrigger simpleTrigger) { scheduler.RescheduleJob(simpleTrigger.Key, simpleTrigger); }
public int InsertExtendedTriggerProperties(ConnectionAndTransactionHolder conn, IOperableTrigger trigger, string state, IJobDetail jobDetail) { ICronTrigger cronTrigger = (ICronTrigger)trigger; using (IDbCommand cmd = DbAccessor.PrepareCommand(conn, AdoJobStoreUtil.ReplaceTablePrefix(StdAdoConstants.SqlInsertCronTrigger, TablePrefix, SchedNameLiteral))) { DbAccessor.AddCommandParameter(cmd, "triggerName", trigger.Key.Name); DbAccessor.AddCommandParameter(cmd, "triggerGroup", trigger.Key.Group); DbAccessor.AddCommandParameter(cmd, "triggerCronExpression", cronTrigger.CronExpressionString); DbAccessor.AddCommandParameter(cmd, "triggerTimeZone", cronTrigger.TimeZone.Id); return(cmd.ExecuteNonQuery()); } }
/// <summary> /// This method has to be implemented in order that starting of the thread causes the object's /// run method to be called in that separately executing thread. /// </summary> /// <param name="cancellationToken">The cancellation instruction.</param> public virtual async Task Run(CancellationToken cancellationToken = default) { Context.CallerId.Value = Guid.NewGuid(); qs !.AddInternalSchedulerListener(this); try { IOperableTrigger trigger = (IOperableTrigger)jec !.Trigger; IJobDetail jobDetail = jec.JobDetail; do { JobExecutionException?jobExEx = null; IJob job = jec.JobInstance; try { Begin(); } catch (SchedulerException se) { string msg = $"Error executing Job {jec.JobDetail.Key}: couldn't begin execution."; await qs.NotifySchedulerListenersError(msg, se, cancellationToken).ConfigureAwait(false); break; } // notify job & trigger listeners... SchedulerInstruction instCode; try { if (!await NotifyListenersBeginning(jec, cancellationToken).ConfigureAwait(false)) { break; } } catch (VetoedException) { try { instCode = trigger.ExecutionComplete(jec, null); await qs.NotifyJobStoreJobVetoed(trigger, jobDetail, instCode, cancellationToken).ConfigureAwait(false); // Even if trigger got vetoed, we still needs to check to see if it's the trigger's finalized run or not. if (jec.Trigger.GetNextFireTimeUtc() == null) { await qs.NotifySchedulerListenersFinalized(jec.Trigger, cancellationToken).ConfigureAwait(false); } Complete(true); } catch (SchedulerException se) { string msg = $"Error during veto of Job {jec.JobDetail.Key}: couldn't finalize execution."; await qs.NotifySchedulerListenersError(msg, se, cancellationToken).ConfigureAwait(false); } break; } DateTimeOffset startTime = SystemTime.UtcNow(); DateTimeOffset endTime; #if DIAGNOSTICS_SOURCE Activity?activity = null; #endif // Execute the job try { if (log.IsDebugEnabled()) { log.Debug("Calling Execute on job " + jobDetail.Key); } #if DIAGNOSTICS_SOURCE activity = jobExecutionJobDiagnostics.WriteStarted(jec, startTime); #endif await job.Execute(jec).ConfigureAwait(false); endTime = SystemTime.UtcNow(); } catch (OperationCanceledException) // handle only scheduler-related cancellations when(cancellationToken.IsCancellationRequested) { endTime = SystemTime.UtcNow(); log.InfoFormat($"Job {jobDetail.Key} was cancelled"); } catch (JobExecutionException jee) { endTime = SystemTime.UtcNow(); jobExEx = jee; #if DIAGNOSTICS_SOURCE jobExecutionJobDiagnostics.WriteException(activity, jobExEx); #endif log.ErrorException($"Job {jobDetail.Key} threw a JobExecutionException: ", jobExEx); } catch (Exception e) { endTime = SystemTime.UtcNow(); log.ErrorException($"Job {jobDetail.Key} threw an unhandled Exception: ", e); SchedulerException se = new SchedulerException("Job threw an unhandled exception.", e); string msg = $"Job {jec.JobDetail.Key} threw an exception."; await qs.NotifySchedulerListenersError(msg, se, cancellationToken).ConfigureAwait(false); jobExEx = new JobExecutionException(se, false); } jec.JobRunTime = endTime - startTime; #if DIAGNOSTICS_SOURCE jobExecutionJobDiagnostics.WriteStopped(activity, endTime, jec); #endif // notify all job listeners if (!await NotifyJobListenersComplete(jec, jobExEx, cancellationToken).ConfigureAwait(false)) { break; } instCode = SchedulerInstruction.NoInstruction; // update the trigger try { instCode = trigger.ExecutionComplete(jec, jobExEx); if (log.IsDebugEnabled()) { log.Debug($"Trigger instruction : {instCode}"); } } catch (Exception e) { // If this happens, there's a bug in the trigger... SchedulerException se = new SchedulerException("Trigger threw an unhandled exception.", e); await qs.NotifySchedulerListenersError("Please report this error to the Quartz developers.", se, cancellationToken).ConfigureAwait(false); } // notify all trigger listeners if (!await NotifyTriggerListenersComplete(jec, instCode, cancellationToken).ConfigureAwait(false)) { break; } // update job/trigger or re-Execute job if (instCode == SchedulerInstruction.ReExecuteJob) { if (log.IsDebugEnabled()) { log.Debug("Rescheduling trigger to reexecute"); } jec.IncrementRefireCount(); try { Complete(false); } catch (SchedulerException se) { await qs.NotifySchedulerListenersError($"Error executing Job {jec.JobDetail.Key}: couldn't finalize execution.", se, cancellationToken).ConfigureAwait(false); } continue; } try { Complete(true); } catch (SchedulerException se) { await qs.NotifySchedulerListenersError($"Error executing Job {jec.JobDetail.Key}: couldn't finalize execution.", se, cancellationToken).ConfigureAwait(false); continue; } await qs.NotifyJobStoreJobComplete(trigger, jobDetail, instCode, cancellationToken).ConfigureAwait(false); break; } while (true); } finally { qs.RemoveInternalSchedulerListener(this); if (jec != null) { if (jec.JobInstance != null) { qs.JobFactory.ReturnJob(jec.JobInstance); } jec.Dispose(); } } }
/// <summary> /// Store the given <see cref="IJobDetail" /> and <see cref="ITrigger" />. /// </summary> /// <param name="newJob">The <see cref="IJobDetail" /> to be stored.</param> /// <param name="newTrigger">The <see cref="ITrigger" /> to be stored.</param> public virtual void StoreJobAndTrigger(IJobDetail newJob, IOperableTrigger newTrigger) { StoreJob(newJob, false); StoreTrigger(newTrigger, false); }
/// <summary> /// Inform the <see cref="IJobStore" /> that the scheduler has completed the /// firing of the given <see cref="ITrigger" /> (and the execution its /// associated <see cref="IJob" />), and that the <see cref="JobDataMap" /> /// in the given <see cref="IJobDetail" /> should be updated if the <see cref="IJob" /> /// is stateful. /// </summary> public virtual void TriggeredJobComplete(IOperableTrigger trigger, IJobDetail jobDetail, SchedulerInstruction triggerInstCode) { lock (lockObject) { this.ReleaseAcquiredTrigger(trigger); // It's possible that the job is null if: // 1- it was deleted during execution // 2- RAMJobStore is being used only for volatile jobs / triggers // from the JDBC job store if (jobDetail.PersistJobDataAfterExecution) { this.Jobs.Update( Query.EQ("_id", jobDetail.Key.ToBsonDocument()), Update.Set("JobDataMap", jobDetail.JobDataMap.ToBsonDocument())); } if (jobDetail.ConcurrentExecutionDisallowed) { IList<Spi.IOperableTrigger> jobTriggers = this.GetTriggersForJob(jobDetail.Key); IEnumerable<BsonDocument> triggerKeys = jobTriggers.Select(t => t.Key.ToBsonDocument()); this.Triggers.Update( Query.And( Query.In("_id", triggerKeys), Query.EQ("State", "Blocked")), Update.Set("State", "Waiting")); this.Triggers.Update( Query.And( Query.In("_id", triggerKeys), Query.EQ("State", "PausedAndBlocked")), Update.Set("State", "Paused")); signaler.SignalSchedulingChange(null); } // even if it was deleted, there may be cleanup to do this.BlockedJobs.Remove( Query.EQ("_id", jobDetail.Key.ToBsonDocument())); // check for trigger deleted during execution... if (triggerInstCode == SchedulerInstruction.DeleteTrigger) { log.Debug("Deleting trigger"); DateTimeOffset? d = trigger.GetNextFireTimeUtc(); if (!d.HasValue) { // double check for possible reschedule within job // execution, which would cancel the need to delete... d = trigger.GetNextFireTimeUtc(); if (!d.HasValue) { this.RemoveTrigger(trigger.Key); } else { log.Debug("Deleting cancelled - trigger still active"); } } else { this.RemoveTrigger(trigger.Key); signaler.SignalSchedulingChange(null); } } else if (triggerInstCode == SchedulerInstruction.SetTriggerComplete) { this.Triggers.Update( Query.EQ("_id", trigger.Key.ToBsonDocument()), Update.Set("State", "Complete")); signaler.SignalSchedulingChange(null); } else if (triggerInstCode == SchedulerInstruction.SetTriggerError) { Log.Info(string.Format(CultureInfo.InvariantCulture, "Trigger {0} set to ERROR state.", trigger.Key)); this.Triggers.Update( Query.EQ("_id", trigger.Key.ToBsonDocument()), Update.Set("State", "Error")); signaler.SignalSchedulingChange(null); } else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersError) { Log.Info(string.Format(CultureInfo.InvariantCulture, "All triggers of Job {0} set to ERROR state.", trigger.JobKey)); IList<Spi.IOperableTrigger> jobTriggers = this.GetTriggersForJob(jobDetail.Key); IEnumerable<BsonDocument> triggerKeys = jobTriggers.Select(t => t.Key.ToBsonDocument()); this.Triggers.Update( Query.In("_id", triggerKeys), Update.Set("State", "Error")); signaler.SignalSchedulingChange(null); } else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersComplete) { IList<Spi.IOperableTrigger> jobTriggers = this.GetTriggersForJob(jobDetail.Key); IEnumerable<BsonDocument> triggerKeys = jobTriggers.Select(t => t.Key.ToBsonDocument()); this.Triggers.Update( Query.In("_id", triggerKeys), Update.Set("State", "Complete")); signaler.SignalSchedulingChange(null); } } }
public async Task TestSelectSimpleTriggerWithDeleteBeforeSelectExtendedProps() { var dbProvider = A.Fake <IDbProvider>(); var connection = A.Fake <DbConnection>(); var transaction = A.Fake <DbTransaction>(); var command = (DbCommand)A.Fake <StubCommand>(); var dbMetadata = new DbMetadata(); A.CallTo(() => dbProvider.Metadata).Returns(dbMetadata); A.CallTo(() => dbProvider.CreateCommand()).Returns(command); var dataReader = A.Fake <DbDataReader>(); A.CallTo(command).Where(x => x.Method.Name == "ExecuteDbDataReaderAsync") .WithReturnType <Task <DbDataReader> >() .Returns(Task.FromResult(dataReader)); A.CallTo(command).Where(x => x.Method.Name == "get_DbParameterCollection") .WithReturnType <DbParameterCollection>() .Returns(new StubParameterCollection()); A.CallTo(() => command.CommandText).Returns(""); A.CallTo(command).Where(x => x.Method.Name == "CreateDbParameter") .WithReturnType <DbParameter>() .Returns(new SqlParameter()); var persistenceDelegate = A.Fake <ITriggerPersistenceDelegate>(); var exception = new InvalidOperationException(); A.CallTo(() => persistenceDelegate.LoadExtendedTriggerProperties(A <ConnectionAndTransactionHolder> .Ignored, A <TriggerKey> .Ignored)).Throws(exception); StdAdoDelegate adoDelegate = new TestStdAdoDelegate(persistenceDelegate); var delegateInitializationArgs = new DelegateInitializationArgs { TablePrefix = "QRTZ_", InstanceId = "TESTSCHED", InstanceName = "INSTANCE", TypeLoadHelper = new SimpleTypeLoadHelper(), UseProperties = false, InitString = "", Logger = LogProvider.GetLogger(GetType()), DbProvider = dbProvider }; adoDelegate.Initialize(delegateInitializationArgs); // First result set has results, second has none A.CallTo(() => dataReader.ReadAsync(CancellationToken.None)).Returns(true).Once(); A.CallTo(() => dataReader[AdoConstants.ColumnTriggerType]).Returns(AdoConstants.TriggerTypeSimple); A.CallTo(() => dataReader[A <string> ._]).Returns("1"); var conn = new ConnectionAndTransactionHolder(connection, transaction); IOperableTrigger trigger = await adoDelegate.SelectTrigger(conn, new TriggerKey("test")); Assert.That(trigger, Is.Null); A.CallTo(() => persistenceDelegate.LoadExtendedTriggerProperties(A <ConnectionAndTransactionHolder> .Ignored, A <TriggerKey> .Ignored)).MustHaveHappened(); }
/// <summary> /// Creates the minimal fired bundle with job detail that has /// given job type. /// </summary> /// <param name="jobType">Type of the job.</param> /// <param name="trigger">The trigger.</param> /// <returns>Minimal TriggerFiredBundle</returns> public static TriggerFiredBundle CreateMinimalFiredBundleWithTypedJobDetail(Type jobType, IOperableTrigger trigger) { JobDetailImpl jd = new JobDetailImpl("jobName", "jobGroup", jobType); TriggerFiredBundle bundle = new TriggerFiredBundle(jd, trigger, null, false, DateTimeOffset.UtcNow, null, null, null); return(bundle); }
public void StoreJobAndTrigger(IJobDetail newJob, IOperableTrigger newTrigger) { throw new NotImplementedException(); }
/// <summary> /// Store the given <see cref="IJobDetail" /> and <see cref="IOperableTrigger" />. /// </summary> /// <param name="newJob">Job to be stored.</param> /// <param name="newTrigger">Trigger to be stored.</param> public void StoreJobAndTrigger(IJobDetail newJob, IOperableTrigger newTrigger) { ExecuteInLock((LockOnInsert) ? LockTriggerAccess : null, conn => { StoreJob(conn, newJob, false); StoreTrigger(conn, newTrigger, newJob, false, StateWaiting, false, false); return null; }); }
/// <summary> /// Store the given <see cref="ITrigger" />. /// </summary> /// <param name="newTrigger">The <see cref="ITrigger" /> to be stored.</param> /// <param name="replaceExisting"> /// If <see langword="true" />, any <see cref="ITrigger" /> existing in /// the <see cref="IJobStore" /> with the same name & group should /// be over-written. /// </param> /// <exception cref="ObjectAlreadyExistsException"> /// if a <see cref="ITrigger" /> with the same name/group already /// exists, and replaceExisting is set to false. /// </exception> public void StoreTrigger(IOperableTrigger newTrigger, bool replaceExisting) { ExecuteInLock( (LockOnInsert || replaceExisting) ? LockTriggerAccess : null, conn => StoreTrigger(conn, newTrigger, null, replaceExisting, StateWaiting, false, false)); }
/// <summary> /// Replaces the trigger. /// </summary> /// <param name="triggerKey">The <see cref="TriggerKey"/> of the <see cref="ITrigger" /> to be replaced.</param> /// <param name="newTrigger">The new trigger.</param> /// <returns></returns> public virtual bool ReplaceTrigger(TriggerKey triggerKey, IOperableTrigger newTrigger) { bool found; lock (lockObject) { IOperableTrigger oldTrigger = this.Triggers.FindOneByIdAs<IOperableTrigger>(triggerKey.ToBsonDocument()); found = oldTrigger != null; if (found) { if (!oldTrigger.JobKey.Equals(newTrigger.JobKey)) { throw new JobPersistenceException("New trigger is not related to the same job as the old trigger."); } this.RemoveTrigger(triggerKey); try { this.StoreTrigger(newTrigger, false); } catch (JobPersistenceException) { this.StoreTrigger(oldTrigger, false); // put previous trigger back... throw; } } } return found; }
/// <summary> /// Inform the <see cref="IJobStore" /> that the scheduler has completed the /// firing of the given <see cref="ITrigger" /> (and the execution its /// associated <see cref="IJob" />), and that the <see cref="JobDataMap" /> /// in the given <see cref="IJobDetail" /> should be updated if the <see cref="IJob" /> /// is stateful. /// </summary> public virtual void TriggeredJobComplete(IOperableTrigger trigger, IJobDetail jobDetail, SchedulerInstruction triggerInstCode) { ExecuteInNonManagedTXLock(LockTriggerAccess, conn => TriggeredJobComplete(conn, trigger, jobDetail, triggerInstCode)); }
/// <summary> /// Store the given <see cref="ITrigger" />. /// </summary> /// <param name="newTrigger">The <see cref="ITrigger" /> to be stored.</param> /// <param name="replaceExisting">If <see langword="true" />, any <see cref="ITrigger" /> existing in /// the <see cref="IJobStore" /> with the same name and group should /// be over-written.</param> public virtual void StoreTrigger(IOperableTrigger newTrigger, bool replaceExisting) { lock (lockObject) { if (this.CheckExists(newTrigger.Key)) { if (!replaceExisting) { throw new ObjectAlreadyExistsException(newTrigger); } // don't delete orphaned job, this trigger has the job anyways this.RemoveTrigger(newTrigger.Key, false); } if (this.RetrieveJob(newTrigger.JobKey) == null) { throw new JobPersistenceException("The job (" + newTrigger.JobKey + ") referenced by the trigger does not exist."); } var document = newTrigger.ToBsonDocument(); string state = "Waiting"; if (this.PausedTriggerGroups.FindOneByIdAs<BsonDocument>(newTrigger.Key.Group) != null || this.PausedJobGroups.FindOneByIdAs<BsonDocument>(newTrigger.JobKey.Group) != null) { state = "Paused"; if (this.BlockedJobs.FindOneByIdAs<BsonDocument>(newTrigger.JobKey.ToBsonDocument()) != null) { state = "PausedAndBlocked"; } } else if (this.BlockedJobs.FindOneByIdAs<BsonDocument>(newTrigger.JobKey.ToBsonDocument()) != null) { state = "Blocked"; } document.Add("State", state); this.Triggers.Save(document); } }
protected virtual void ReleaseAcquiredTrigger(ConnectionAndTransactionHolder conn, IOperableTrigger trigger) { try { Delegate.UpdateTriggerStateFromOtherState(conn, trigger.Key, StateWaiting, StateAcquired); Delegate.DeleteFiredTrigger(conn, trigger.FireInstanceId); } catch (Exception e) { throw new JobPersistenceException("Couldn't release acquired trigger: " + e.Message, e); } }
/// <summary> /// Applies the misfire. /// </summary> /// <param name="tw">The trigger wrapper.</param> /// <returns></returns> protected virtual bool ApplyMisfire(IOperableTrigger trigger) { DateTimeOffset misfireTime = SystemTime.UtcNow(); if (MisfireThreshold > TimeSpan.Zero) { misfireTime = misfireTime.AddMilliseconds(-1 * MisfireThreshold.TotalMilliseconds); } DateTimeOffset? tnft = trigger.GetNextFireTimeUtc(); if (!tnft.HasValue || tnft.Value > misfireTime || trigger.MisfireInstruction == MisfireInstruction.IgnoreMisfirePolicy) { return false; } ICalendar cal = null; if (trigger.CalendarName != null) { cal = this.RetrieveCalendar(trigger.CalendarName); } signaler.NotifyTriggerListenersMisfired(trigger); trigger.UpdateAfterMisfire(cal); this.StoreTrigger(trigger, true); if (!trigger.GetNextFireTimeUtc().HasValue) { this.Triggers.Update( Query.EQ("_id", trigger.Key.ToBsonDocument()), Update.Set("State", "Complete")); signaler.NotifySchedulerListenersFinalized(trigger); } else if (tnft.Equals(trigger.GetNextFireTimeUtc())) { return false; } return true; }
public static string GetDatabaseId(this IOperableTrigger trigger) { return($"{trigger.Key.Name}/{trigger.Key.Group}"); }
/// <summary> /// Store and schedule the identified <see cref="IOperableTrigger"/> /// </summary> /// <param name="trig"></param> public void TriggerJob(IOperableTrigger trig) { ValidateState(); trig.ComputeFirstFireTimeUtc(null); bool collision = true; while (collision) { try { resources.JobStore.StoreTrigger(trig, false); collision = false; } catch (ObjectAlreadyExistsException) { trig.Key = new TriggerKey(NewTriggerId(), SchedulerConstants.DefaultGroup); } } NotifySchedulerThread(trig.GetNextFireTimeUtc()); NotifySchedulerListenersScheduled(trig); }
public bool CanHandleTriggerType(IOperableTrigger trigger) { return(trigger is CronTriggerImpl impl && !impl.HasAdditionalProperties); }
/// <summary> /// The main processing loop of the <see cref="QuartzSchedulerThread" />. /// </summary> public override void Run() { bool lastAcquireFailed = false; while (!halted) { try { // check if we're supposed to pause... lock (sigLock) { while (paused && !halted) { try { // wait until togglePause(false) is called... Monitor.Wait(sigLock, 1000); } catch (ThreadInterruptedException) { } } if (halted) { break; } } int availThreadCount = qsRsrcs.ThreadPool.BlockForAvailableThreads(); if (availThreadCount > 0) // will always be true, due to semantics of blockForAvailableThreads... { IList <IOperableTrigger> triggers = null; DateTimeOffset now = SystemTime.UtcNow(); ClearSignaledSchedulingChange(); try { triggers = qsRsrcs.JobStore.AcquireNextTriggers( now + idleWaitTime, Math.Min(availThreadCount, qsRsrcs.MaxBatchSize), qsRsrcs.BatchTimeWindow); lastAcquireFailed = false; if (log.IsDebugEnabled) { log.DebugFormat("Batch acquisition of {0} triggers", (triggers == null ? 0 : triggers.Count)); } } catch (JobPersistenceException jpe) { if (!lastAcquireFailed) { qs.NotifySchedulerListenersError("An error occurred while scanning for the next trigger to fire.", jpe); } lastAcquireFailed = true; continue; } catch (Exception e) { if (!lastAcquireFailed) { Log.Error("quartzSchedulerThreadLoop: RuntimeException " + e.Message, e); } lastAcquireFailed = true; continue; } if (triggers != null && triggers.Count > 0) { now = SystemTime.UtcNow(); DateTimeOffset triggerTime = triggers[0].GetNextFireTimeUtc().Value; TimeSpan timeUntilTrigger = triggerTime - now; while (timeUntilTrigger > TimeSpan.FromMilliseconds(2)) { if (ReleaseIfScheduleChangedSignificantly(triggers, triggerTime)) { break; } lock (sigLock) { if (halted) { break; } if (!IsCandidateNewTimeEarlierWithinReason(triggerTime, false)) { try { // we could have blocked a long while // on 'synchronize', so we must recompute now = SystemTime.UtcNow(); timeUntilTrigger = triggerTime - now; if (timeUntilTrigger > TimeSpan.Zero) { Monitor.Wait(sigLock, timeUntilTrigger); } } catch (ThreadInterruptedException) { } } } if (ReleaseIfScheduleChangedSignificantly(triggers, triggerTime)) { break; } now = SystemTime.UtcNow(); timeUntilTrigger = triggerTime - now; } // this happens if releaseIfScheduleChangedSignificantly decided to release triggers if (triggers.Count == 0) { continue; } // set triggers to 'executing' IList <TriggerFiredResult> bndles = new List <TriggerFiredResult>(); bool goAhead = true; lock (sigLock) { goAhead = !halted; } if (goAhead) { try { IList <TriggerFiredResult> res = qsRsrcs.JobStore.TriggersFired(triggers); if (res != null) { bndles = res; } } catch (SchedulerException se) { qs.NotifySchedulerListenersError("An error occurred while firing triggers '" + triggers + "'", se); // QTZ-179 : a problem occurred interacting with the triggers from the db // we release them and loop again foreach (IOperableTrigger t in triggers) { qsRsrcs.JobStore.ReleaseAcquiredTrigger(t); } continue; } } for (int i = 0; i < bndles.Count; i++) { TriggerFiredResult result = bndles[i]; TriggerFiredBundle bndle = result.TriggerFiredBundle; Exception exception = result.Exception; IOperableTrigger trigger = triggers[i]; // TODO SQL exception? if (exception != null && (exception is DbException || exception.InnerException is DbException)) { Log.Error("DbException while firing trigger " + trigger, exception); qsRsrcs.JobStore.ReleaseAcquiredTrigger(trigger); continue; } // it's possible to get 'null' if the triggers was paused, // blocked, or other similar occurrences that prevent it being // fired at this time... or if the scheduler was shutdown (halted) if (bndle == null) { qsRsrcs.JobStore.ReleaseAcquiredTrigger(trigger); continue; } // TODO: improvements: // // 2- make sure we can get a job runshell before firing trigger, or // don't let that throw an exception (right now it never does, // but the signature says it can). // 3- acquire more triggers at a time (based on num threads available?) JobRunShell shell = null; try { shell = qsRsrcs.JobRunShellFactory.CreateJobRunShell(bndle); shell.Initialize(qs); } catch (SchedulerException) { qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError); continue; } if (qsRsrcs.ThreadPool.RunInThread(shell) == false) { // this case should never happen, as it is indicative of the // scheduler being shutdown or a bug in the thread pool or // a thread pool being used concurrently - which the docs // say not to do... Log.Error("ThreadPool.runInThread() return false!"); qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError); } } continue; // while (!halted) } } else // if(availThreadCount > 0) { // should never happen, if threadPool.blockForAvailableThreads() follows contract continue; // while (!halted) } DateTimeOffset utcNow = SystemTime.UtcNow(); DateTimeOffset waitTime = utcNow.Add(GetRandomizedIdleWaitTime()); TimeSpan timeUntilContinue = waitTime - utcNow; lock (sigLock) { if (!halted) { try { // QTZ-336 A job might have been completed in the mean time and we might have // missed the scheduled changed signal by not waiting for the notify() yet // Check that before waiting for too long in case this very job needs to be // scheduled very soon if (!IsScheduleChanged()) { Monitor.Wait(sigLock, timeUntilContinue); } } catch (ThreadInterruptedException) { } } } } catch (Exception re) { if (Log != null) { Log.Error("Runtime error occurred in main trigger firing loop.", re); } } } // while (!halted) // drop references to scheduler stuff to aid garbage collection... qs = null; qsRsrcs = null; }
/// <see cref="IJobStore.ReplaceTrigger(TriggerKey, IOperableTrigger)" /> public bool ReplaceTrigger(TriggerKey triggerKey, IOperableTrigger newTrigger) { return (bool) ExecuteInLock(LockTriggerAccess, conn => ReplaceTrigger(conn, triggerKey, newTrigger)); }