/// <summary> /// Get a handle to the next trigger to be fired, and mark it as 'reserved' /// by the calling scheduler. /// </summary> /// <seealso cref="ITrigger" /> public virtual IList<IOperableTrigger> AcquireNextTriggers(DateTimeOffset noLaterThan, int maxCount, TimeSpan timeWindow) { lock (lockObject) { // multiple instances management this.Schedulers.Save(new BsonDocument( new BsonElement("_id", this.instanceId), new BsonElement("Expires", (SystemTime.Now() + new TimeSpan(0, 10, 0)).UtcDateTime), new BsonElement("State", "Running"))); this.Schedulers.Remove( Query.LT("Expires", SystemTime.Now().UtcDateTime)); IEnumerable<BsonValue> activeInstances = this.Schedulers.Distinct("_id"); this.Triggers.Update( Query.NotIn("SchedulerInstanceId", activeInstances), Update.Unset("SchedulerInstanceId") .Set("State", "Waiting")); List<IOperableTrigger> result = new List<IOperableTrigger>(); Collection.ISet<JobKey> acquiredJobKeysForNoConcurrentExec = new Collection.HashSet<JobKey>(); DateTimeOffset? firstAcquiredTriggerFireTime = null; var candidates = this.Triggers.FindAs<Spi.IOperableTrigger>( Query.And( Query.EQ("State", "Waiting"), Query.LTE("nextFireTimeUtc", (noLaterThan + timeWindow).UtcDateTime))) .OrderBy(t => t.GetNextFireTimeUtc()).ThenByDescending(t => t.Priority); foreach (IOperableTrigger trigger in candidates) { if (trigger.GetNextFireTimeUtc() == null) { continue; } // it's possible that we've selected triggers way outside of the max fire ahead time for batches // (up to idleWaitTime + fireAheadTime) so we need to make sure not to include such triggers. // So we select from the first next trigger to fire up until the max fire ahead time after that... // which will perfectly honor the fireAheadTime window because the no firing will occur until // the first acquired trigger's fire time arrives. if (firstAcquiredTriggerFireTime != null && trigger.GetNextFireTimeUtc() > (firstAcquiredTriggerFireTime.Value + timeWindow)) { break; } if (this.ApplyMisfire(trigger)) { if (trigger.GetNextFireTimeUtc() == null || trigger.GetNextFireTimeUtc() > noLaterThan + timeWindow) { continue; } } // If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then // put it back into the timeTriggers set and continue to search for next trigger. JobKey jobKey = trigger.JobKey; IJobDetail job = this.Jobs.FindOneByIdAs<IJobDetail>(jobKey.ToBsonDocument()); if (job.ConcurrentExecutionDisallowed) { if (acquiredJobKeysForNoConcurrentExec.Contains(jobKey)) { continue; // go to next trigger in store. } else { acquiredJobKeysForNoConcurrentExec.Add(jobKey); } } trigger.FireInstanceId = this.GetFiredTriggerRecordId(); var acquired = this.Triggers.FindAndModify( Query.And( Query.EQ("_id", trigger.Key.ToBsonDocument()), Query.EQ("State", "Waiting")), SortBy.Null, Update.Set("State", "Acquired") .Set("SchedulerInstanceId", this.instanceId) .Set("FireInstanceId", trigger.FireInstanceId)); if (acquired.ModifiedDocument != null) { result.Add(trigger); if (firstAcquiredTriggerFireTime == null) { firstAcquiredTriggerFireTime = trigger.GetNextFireTimeUtc(); } } if (result.Count == maxCount) { break; } } return result; } }
/// <summary> /// Gets the property groups. /// </summary> /// <param name="prefix">The prefix.</param> /// <returns></returns> public virtual IList<string> GetPropertyGroups(string prefix) { var groups = new Collection.HashSet<string>(); if (!prefix.EndsWith(".")) { prefix += "."; } foreach (string key in props.Keys) { if (key.StartsWith(prefix)) { string groupName = key.Substring(prefix.Length, (key.IndexOf('.', prefix.Length)) - (prefix.Length)); groups.Add(groupName); } } return new List<string>(groups); }
/// <summary> /// Resume (un-pause) all of the <see cref="IJobDetail" />s /// in the given group. /// <para> /// If any of the <see cref="IJob" /> s had <see cref="ITrigger" /> s that /// missed one or more fire-times, then the <see cref="ITrigger" />'s /// misfire instruction will be applied. /// </para> /// </summary> public virtual Collection.ISet<string> ResumeJobs(GroupMatcher<JobKey> matcher) { Collection.ISet<string> resumedGroups = new Collection.HashSet<string>(); lock (lockObject) { Collection.ISet<JobKey> keys = GetJobKeys(matcher); foreach (string pausedJobGroup in this.PausedJobGroups.FindAllAs<string>()) { if (matcher.CompareWithOperator.Evaluate(pausedJobGroup, matcher.CompareToValue)) { resumedGroups.Add(pausedJobGroup); } } this.PausedTriggerGroups.Remove( Query.All("_id", new BsonArray(resumedGroups))); foreach (JobKey key in keys) { IList<IOperableTrigger> triggers = GetTriggersForJob(key); foreach (IOperableTrigger trigger in triggers) { ResumeTrigger(trigger.Key); } } } return resumedGroups; }
/// <summary> /// Resume (un-pause) all of the <see cref="ITrigger" />s in the /// given group. /// <para> /// If any <see cref="ITrigger" /> missed one or more fire-times, then the /// <see cref="ITrigger" />'s misfire instruction will be applied. /// </para> /// </summary> public virtual IList<string> ResumeTriggers(GroupMatcher<TriggerKey> matcher) { Collection.ISet<string> groups = new Collection.HashSet<string>(); lock (lockObject) { Collection.ISet<TriggerKey> keys = this.GetTriggerKeys(matcher); foreach (TriggerKey triggerKey in keys) { groups.Add(triggerKey.Group); IOperableTrigger trigger = this.Triggers.FindOneByIdAs<IOperableTrigger>(triggerKey.ToBsonDocument()); var pausedJobGroup = this.PausedJobGroups.FindOneByIdAs<string>(trigger.JobKey.Group); if (pausedJobGroup != null) { continue; } this.ResumeTrigger(triggerKey); } foreach (String group in groups) { this.PausedTriggerGroups.Remove( Query.EQ("_id", group)); } } return new List<string>(groups); }
protected virtual void ClusterRecover(ConnectionAndTransactionHolder conn, IList<SchedulerStateRecord> failedInstances) { if (failedInstances.Count > 0) { long recoverIds = SystemTime.UtcNow().Ticks; LogWarnIfNonZero(failedInstances.Count, "ClusterManager: detected " + failedInstances.Count + " failed or restarted instances."); try { foreach (SchedulerStateRecord rec in failedInstances) { Log.Info("ClusterManager: Scanning for instance \"" + rec.SchedulerInstanceId + "\"'s failed in-progress jobs."); IList<FiredTriggerRecord> firedTriggerRecs = Delegate.SelectInstancesFiredTriggerRecords(conn, rec.SchedulerInstanceId); int acquiredCount = 0; int recoveredCount = 0; int otherCount = 0; Collection.HashSet<TriggerKey> triggerKeys = new Collection.HashSet<TriggerKey>(); foreach (FiredTriggerRecord ftRec in firedTriggerRecs) { TriggerKey tKey = ftRec.TriggerKey; JobKey jKey = ftRec.JobKey; triggerKeys.Add(tKey); // release blocked triggers.. if (ftRec.FireInstanceState.Equals(StateBlocked)) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey, StateWaiting, StateBlocked); } else if (ftRec.FireInstanceState.Equals(StatePausedBlocked)) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey, StatePaused, StatePausedBlocked); } // release acquired triggers.. if (ftRec.FireInstanceState.Equals(StateAcquired)) { Delegate.UpdateTriggerStateFromOtherState(conn, tKey, StateWaiting, StateAcquired); acquiredCount++; } else if (ftRec.JobRequestsRecovery) { // handle jobs marked for recovery that were not fully // executed.. if (JobExists(conn, jKey)) { SimpleTriggerImpl rcvryTrig = new SimpleTriggerImpl( "recover_" + rec.SchedulerInstanceId + "_" + Convert.ToString(recoverIds++, CultureInfo.InvariantCulture), SchedulerConstants.DefaultRecoveryGroup, ftRec.FireTimestamp); rcvryTrig.JobName = jKey.Name; rcvryTrig.JobGroup = jKey.Group; rcvryTrig.MisfireInstruction = MisfireInstruction.SimpleTrigger.FireNow; rcvryTrig.Priority = ftRec.Priority; JobDataMap jd = Delegate.SelectTriggerJobDataMap(conn, tKey); jd.Put(SchedulerConstants.FailedJobOriginalTriggerName, tKey.Name); jd.Put(SchedulerConstants.FailedJobOriginalTriggerGroup, tKey.Group); jd.Put(SchedulerConstants.FailedJobOriginalTriggerFiretimeInMillisecoonds, Convert.ToString(ftRec.FireTimestamp, CultureInfo.InvariantCulture)); rcvryTrig.JobDataMap = jd; rcvryTrig.ComputeFirstFireTimeUtc(null); StoreTrigger(conn, rcvryTrig, null, false, StateWaiting, false, true); recoveredCount++; } else { Log.Warn("ClusterManager: failed job '" + jKey + "' no longer exists, cannot schedule recovery."); otherCount++; } } else { otherCount++; } // free up stateful job's triggers if (ftRec.JobDisallowsConcurrentExecution) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey, StateWaiting, StateBlocked); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey, StatePaused, StatePausedBlocked); } } Delegate.DeleteFiredTriggers(conn, rec.SchedulerInstanceId); // Check if any of the fired triggers we just deleted were the last fired trigger // records of a COMPLETE trigger. int completeCount = 0; foreach (TriggerKey triggerKey in triggerKeys) { if ( Delegate.SelectTriggerState(conn, triggerKey).Equals(StateComplete)) { IList<FiredTriggerRecord> firedTriggers = Delegate.SelectFiredTriggerRecords(conn, triggerKey.Name, triggerKey.Group); if (firedTriggers.Count == 0) { if (RemoveTrigger(conn, triggerKey)) { completeCount++; } } } } LogWarnIfNonZero(acquiredCount, "ClusterManager: ......Freed " + acquiredCount + " acquired trigger(s)."); LogWarnIfNonZero(completeCount, "ClusterManager: ......Deleted " + completeCount + " complete triggers(s)."); LogWarnIfNonZero(recoveredCount, "ClusterManager: ......Scheduled " + recoveredCount + " recoverable job(s) for recovery."); LogWarnIfNonZero(otherCount, "ClusterManager: ......Cleaned-up " + otherCount + " other failed job(s)."); if (rec.SchedulerInstanceId.Equals(InstanceId) == false) { Delegate.DeleteSchedulerState(conn, rec.SchedulerInstanceId); } } } catch (Exception e) { throw new JobPersistenceException("Failure recovering jobs: " + e.Message, e); } } }
/// <summary> /// Pause all of the <see cref="IJob" />s in the given /// group - by pausing all of their <see cref="ITrigger" />s. /// </summary> /// <seealso cref="ResumeJobs" /> public virtual IList<string> PauseJobs(GroupMatcher<JobKey> matcher) { return (IList<string>)ExecuteInLock(LockTriggerAccess, conn => { Collection.ISet<string> groupNames = new Collection.HashSet<string>(); Collection.ISet<JobKey> jobNames = GetJobNames(conn, matcher); foreach (JobKey jobKey in jobNames) { IList<IOperableTrigger> triggers = GetTriggersForJob(conn, jobKey); foreach (IOperableTrigger trigger in triggers) { PauseTrigger(conn, trigger.Key); } groupNames.Add(jobKey.Group); } return new List<string>(groupNames); }); }
/// <summary> /// Resume (un-pause) all of the <see cref="ITrigger" />s /// in the given group. /// <para> /// If any <see cref="ITrigger" /> missed one or more fire-times, then the /// <see cref="ITrigger" />'s misfire instruction will be applied. /// </para> /// </summary> public virtual IList<string> ResumeTriggers(ConnectionAndTransactionHolder conn, GroupMatcher<TriggerKey> matcher) { try { Delegate.DeletePausedTriggerGroup(conn, matcher); Collection.HashSet<string> groups = new Collection.HashSet<string>(); Collection.ISet<TriggerKey> keys = Delegate.SelectTriggersInGroup(conn, matcher); foreach (TriggerKey key in keys) { ResumeTrigger(conn, key); groups.Add(key.Group); } return new List<string>(groups); // TODO: find an efficient way to resume triggers (better than the // above)... logic below is broken because of // findTriggersToBeBlocked() /* * int res = * getDelegate().UpdateTriggerGroupStateFromOtherState(conn, * groupName, StateWaiting, StatePaused); * * if(res > 0) { * * long misfireTime = System.currentTimeMillis(); * if(getMisfireThreshold() > 0) misfireTime -= * getMisfireThreshold(); * * Key[] misfires = * getDelegate().SelectMisfiredTriggersInGroupInState(conn, * groupName, StateWaiting, misfireTime); * * List blockedTriggers = findTriggersToBeBlocked(conn, * groupName); * * Iterator itr = blockedTriggers.iterator(); while(itr.hasNext()) { * Key key = (Key)itr.next(); * getDelegate().UpdateTriggerState(conn, key.getName(), * key.getGroup(), StateBlocked); } * * for(int i=0; i < misfires.length; i++) { String * newState = StateWaiting; * if(blockedTriggers.contains(misfires[i])) newState = * StateBlocked; UpdateMisfiredTrigger(conn, * misfires[i].getName(), misfires[i].getGroup(), newState, true); } } */ } catch (Exception e) { throw new JobPersistenceException("Couldn't pause trigger group '" + matcher + "': " + e.Message, e); } }
// TODO: this really ought to return something like a FiredTriggerBundle, // so that the fireInstanceId doesn't have to be on the trigger... protected virtual IList<IOperableTrigger> AcquireNextTrigger(ConnectionAndTransactionHolder conn, DateTimeOffset noLaterThan, int maxCount, TimeSpan timeWindow) { List<IOperableTrigger> acquiredTriggers = new List<IOperableTrigger>(); Collection.ISet<JobKey> acquiredJobKeysForNoConcurrentExec = new Collection.HashSet<JobKey>(); const int MaxDoLoopRetry = 3; int currentLoopCount = 0; DateTimeOffset? firstAcquiredTriggerFireTime = null; do { currentLoopCount ++; try { IList<TriggerKey> keys; // If timeWindow is specified, then we need to select trigger fire time with wider range! if (timeWindow > TimeSpan.Zero) { keys = Delegate.SelectTriggerToAcquire(conn, noLaterThan + timeWindow, MisfireTime, maxCount); } else { keys = Delegate.SelectTriggerToAcquire(conn, noLaterThan, MisfireTime, maxCount); } // No trigger is ready to fire yet. if (keys == null || keys.Count == 0) { return acquiredTriggers; } foreach (TriggerKey triggerKey in keys) { // If our trigger is no longer available, try a new one. IOperableTrigger nextTrigger = RetrieveTrigger(conn, triggerKey); if (nextTrigger == null) { continue; // next trigger } // it's possible that we've selected triggers way outside of the max fire ahead time for batches // (up to idleWaitTime + fireAheadTime) so we need to make sure not to include such triggers. // So we select from the first next trigger to fire up until the max fire ahead time after that... // which will perfectly honor the fireAheadTime window because the no firing will occur until // the first acquired trigger's fire time arrives. if (firstAcquiredTriggerFireTime != null && nextTrigger.GetNextFireTimeUtc() > (firstAcquiredTriggerFireTime.Value + timeWindow)) { break; } // If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then // put it back into the timeTriggers set and continue to search for next trigger. JobKey jobKey = nextTrigger.JobKey; IJobDetail job = Delegate.SelectJobDetail(conn, jobKey, TypeLoadHelper); if (job.ConcurrentExecutionDisallowed) { if (acquiredJobKeysForNoConcurrentExec.Contains(jobKey)) { continue; // next trigger } else { acquiredJobKeysForNoConcurrentExec.Add(jobKey); } } // We now have a acquired trigger, let's add to return list. // If our trigger was no longer in the expected state, try a new one. int rowsUpdated = Delegate.UpdateTriggerStateFromOtherState(conn, triggerKey, StateAcquired, StateWaiting); if (rowsUpdated <= 0) { // TODO: Hum... shouldn't we log a warning here? continue; // next trigger } nextTrigger.FireInstanceId = GetFiredTriggerRecordId(); Delegate.InsertFiredTrigger(conn, nextTrigger, StateAcquired, null); acquiredTriggers.Add(nextTrigger); if (firstAcquiredTriggerFireTime == null) { firstAcquiredTriggerFireTime = nextTrigger.GetNextFireTimeUtc(); } } // if we didn't end up with any trigger to fire from that first // batch, try again for another batch. We allow with a max retry count. if (acquiredTriggers.Count == 0 && currentLoopCount < MaxDoLoopRetry) { continue; } // We are done with the while loop. break; } catch (Exception e) { throw new JobPersistenceException("Couldn't acquire next trigger: " + e.Message, e); } } while (true); // Return the acquired trigger list return acquiredTriggers; }
public void TestStartTimeBeforeStartTimeOfDayOnInvalidDay() { DateTimeOffset startTime = dateOf(0, 0, 0, 1, 1, 2011); // Jan 1, 2011 was a saturday... TimeOfDay startTimeOfDay = new TimeOfDay(8, 0, 0); var trigger = new DailyTimeIntervalTriggerImpl(); var daysOfWeek = new Collection.HashSet<DayOfWeek> { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }; trigger.DaysOfWeek = daysOfWeek; trigger.StartTimeUtc = startTime.ToUniversalTime(); trigger.StartTimeOfDay = startTimeOfDay; trigger.RepeatIntervalUnit = IntervalUnit.Minute; trigger.RepeatInterval = 60; Assert.That(trigger.GetFireTimeAfter(dateOf(6, 0, 0, 22, 5, 2010)), Is.EqualTo(dateOf(8, 0, 0, 3, 1, 2011))); var fireTimes = TriggerUtils.ComputeFireTimes(trigger, null, 48); Assert.That(fireTimes.Count, Is.EqualTo(48)); Assert.That(fireTimes[0], Is.EqualTo(dateOf(8, 0, 0, 3, 1, 2011))); Assert.That(fireTimes[47], Is.EqualTo(dateOf(23, 0, 0, 5, 1, 2011))); }
public void TestMonOnly() { Collection.ISet<DayOfWeek> daysOfWeek = new Collection.HashSet<DayOfWeek>(); daysOfWeek.Add(DayOfWeek.Monday); DateTimeOffset startTime = DateBuilder.DateOf(0, 0, 0, 1, 1, 2011); // SAT(7) TimeOfDay startTimeOfDay = new TimeOfDay(8, 0, 0); TimeOfDay endTimeOfDay = new TimeOfDay(17, 0, 0); var trigger = new DailyTimeIntervalTriggerImpl { StartTimeUtc = startTime.ToUniversalTime(), StartTimeOfDay = startTimeOfDay, EndTimeOfDay = endTimeOfDay, DaysOfWeek = daysOfWeek, RepeatIntervalUnit = IntervalUnit.Minute, RepeatInterval = 60 }; IList<DateTimeOffset> fireTimes = TriggerUtils.ComputeFireTimes(trigger, null, 48); Assert.AreEqual(48, fireTimes.Count); Assert.AreEqual(DateBuilder.DateOf(8, 0, 0, 3, 1, 2011), fireTimes[0]); Assert.AreEqual(DayOfWeek.Monday, fireTimes[0].DayOfWeek); Assert.AreEqual(DateBuilder.DateOf(8, 0, 0, 10, 1, 2011), fireTimes[10]); Assert.AreEqual(DayOfWeek.Monday, fireTimes[10].DayOfWeek); Assert.AreEqual(DateBuilder.DateOf(15, 0, 0, 31, 1, 2011), fireTimes[47]); Assert.AreEqual(DayOfWeek.Monday, fireTimes[47].DayOfWeek); }
public void Test(IScheduler scheduler, bool clearJobs, bool scheduleJobs) { try { if (clearJobs) { scheduler.Clear(); } if (scheduleJobs) { ICalendar cronCalendar = new CronCalendar("0/5 * * * * ?"); ICalendar holidayCalendar = new HolidayCalendar(); // QRTZNET-86 ITrigger t = scheduler.GetTrigger(new TriggerKey("NonExistingTrigger", "NonExistingGroup")); Assert.IsNull(t); AnnualCalendar cal = new AnnualCalendar(); scheduler.AddCalendar("annualCalendar", cal, false, true); IOperableTrigger calendarsTrigger = new SimpleTriggerImpl("calendarsTrigger", "test", 20, TimeSpan.FromMilliseconds(5)); calendarsTrigger.CalendarName = "annualCalendar"; JobDetailImpl jd = new JobDetailImpl("testJob", "test", typeof(NoOpJob)); scheduler.ScheduleJob(jd, calendarsTrigger); // QRTZNET-93 scheduler.AddCalendar("annualCalendar", cal, true, true); scheduler.AddCalendar("baseCalendar", new BaseCalendar(), false, true); scheduler.AddCalendar("cronCalendar", cronCalendar, false, true); scheduler.AddCalendar("dailyCalendar", new DailyCalendar(DateTime.Now.Date, DateTime.Now.AddMinutes(1)), false, true); scheduler.AddCalendar("holidayCalendar", holidayCalendar, false, true); scheduler.AddCalendar("monthlyCalendar", new MonthlyCalendar(), false, true); scheduler.AddCalendar("weeklyCalendar", new WeeklyCalendar(), false, true); scheduler.AddCalendar("cronCalendar", cronCalendar, true, true); scheduler.AddCalendar("holidayCalendar", holidayCalendar, true, true); Assert.IsNotNull(scheduler.GetCalendar("annualCalendar")); JobDetailImpl lonelyJob = new JobDetailImpl("lonelyJob", "lonelyGroup", typeof(SimpleRecoveryJob)); lonelyJob.Durable = true; lonelyJob.RequestsRecovery = true; scheduler.AddJob(lonelyJob, false); scheduler.AddJob(lonelyJob, true); string schedId = scheduler.SchedulerInstanceId; int count = 1; JobDetailImpl job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = true; IOperableTrigger trigger = new SimpleTriggerImpl("trig_" + count, schedId, 20, TimeSpan.FromSeconds(5)); trigger.JobDataMap.Add("key", "value"); trigger.EndTimeUtc = DateTime.UtcNow.AddYears(10); trigger.StartTimeUtc = DateTime.Now.AddMilliseconds(1000L); scheduler.ScheduleJob(job, trigger); // check that trigger was stored ITrigger persisted = scheduler.GetTrigger(new TriggerKey("trig_" + count, schedId)); Assert.IsNotNull(persisted); Assert.IsTrue(persisted is SimpleTriggerImpl); count++; job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = (true); trigger = new SimpleTriggerImpl("trig_" + count, schedId, 20, TimeSpan.FromSeconds(5)); trigger.StartTimeUtc = (DateTime.Now.AddMilliseconds(2000L)); scheduler.ScheduleJob(job, trigger); count++; job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryStatefulJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = (true); trigger = new SimpleTriggerImpl("trig_" + count, schedId, 20, TimeSpan.FromSeconds(3)); trigger.StartTimeUtc = (DateTime.Now.AddMilliseconds(1000L)); scheduler.ScheduleJob(job, trigger); count++; job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = (true); trigger = new SimpleTriggerImpl("trig_" + count, schedId, 20, TimeSpan.FromSeconds(4)); trigger.StartTimeUtc = (DateTime.Now.AddMilliseconds(1000L)); scheduler.ScheduleJob(job, trigger); count++; job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = (true); trigger = new SimpleTriggerImpl("trig_" + count, schedId, 20, TimeSpan.FromMilliseconds(4500)); scheduler.ScheduleJob(job, trigger); count++; job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = (true); IOperableTrigger ct = new CronTriggerImpl("cron_trig_" + count, schedId, "0/10 * * * * ?"); ct.JobDataMap.Add("key", "value"); ct.StartTimeUtc = DateTime.Now.AddMilliseconds(1000); scheduler.ScheduleJob(job, ct); count++; job = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); // ask scheduler to re-Execute this job if it was in progress when // the scheduler went down... job.RequestsRecovery = (true); DailyTimeIntervalTriggerImpl nt = new DailyTimeIntervalTriggerImpl("nth_trig_" + count, schedId, new TimeOfDay(1, 1, 1), new TimeOfDay(23, 30, 0), IntervalUnit.Hour, 1); nt.StartTimeUtc = DateTime.Now.Date.AddMilliseconds(1000); scheduler.ScheduleJob(job, nt); DailyTimeIntervalTriggerImpl nt2 = new DailyTimeIntervalTriggerImpl(); nt2.Key = new TriggerKey("nth_trig2_" + count, schedId); nt2.StartTimeUtc = DateTime.Now.Date.AddMilliseconds(1000); nt2.JobKey = job.Key; scheduler.ScheduleJob(nt2); // GitHub issue #92 scheduler.GetTrigger(nt2.Key); // GitHub issue #98 nt2.StartTimeOfDay = new TimeOfDay(1, 2, 3); nt2.EndTimeOfDay = new TimeOfDay(2, 3, 4); scheduler.UnscheduleJob(nt2.Key); scheduler.ScheduleJob(nt2); var triggerFromDb = (IDailyTimeIntervalTrigger) scheduler.GetTrigger(nt2.Key); Assert.That(triggerFromDb.StartTimeOfDay.Hour, Is.EqualTo(1)); Assert.That(triggerFromDb.StartTimeOfDay.Minute, Is.EqualTo(2)); Assert.That(triggerFromDb.StartTimeOfDay.Second, Is.EqualTo(3)); Assert.That(triggerFromDb.EndTimeOfDay.Hour, Is.EqualTo(2)); Assert.That(triggerFromDb.EndTimeOfDay.Minute, Is.EqualTo(3)); Assert.That(triggerFromDb.EndTimeOfDay.Second, Is.EqualTo(4)); job.RequestsRecovery = (true); CalendarIntervalTriggerImpl intervalTrigger = new CalendarIntervalTriggerImpl( "calint_trig_" + count, schedId, DateTime.UtcNow.AddMilliseconds(300), DateTime.UtcNow.AddMinutes(1), IntervalUnit.Second, 8); intervalTrigger.JobKey = job.Key; scheduler.ScheduleJob(intervalTrigger); // bulk operations var info = new Dictionary<IJobDetail, Collection.ISet<ITrigger>>(); IJobDetail detail = new JobDetailImpl("job_" + count, schedId, typeof(SimpleRecoveryJob)); ITrigger simple = new SimpleTriggerImpl("trig_" + count, schedId, 20, TimeSpan.FromMilliseconds(4500)); var triggers = new Collection.HashSet<ITrigger>(); triggers.Add(simple); info[detail] = triggers; scheduler.ScheduleJobs(info, true); Assert.IsTrue(scheduler.CheckExists(detail.Key)); Assert.IsTrue(scheduler.CheckExists(simple.Key)); // QRTZNET-243 scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupContains("a").DeepClone()); scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEndsWith("a").DeepClone()); scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupStartsWith("a").DeepClone()); scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals("a").DeepClone()); scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupContains("a").DeepClone()); scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEndsWith("a").DeepClone()); scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupStartsWith("a").DeepClone()); scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals("a").DeepClone()); scheduler.Start(); Thread.Sleep(TimeSpan.FromSeconds(3)); scheduler.PauseAll(); scheduler.ResumeAll(); scheduler.PauseJob(new JobKey("job_1", schedId)); scheduler.ResumeJob(new JobKey("job_1", schedId)); scheduler.PauseJobs(GroupMatcher<JobKey>.GroupEquals(schedId)); Thread.Sleep(TimeSpan.FromSeconds(1)); scheduler.ResumeJobs(GroupMatcher<JobKey>.GroupEquals(schedId)); scheduler.PauseTrigger(new TriggerKey("trig_2", schedId)); scheduler.ResumeTrigger(new TriggerKey("trig_2", schedId)); scheduler.PauseTriggers(GroupMatcher<TriggerKey>.GroupEquals(schedId)); Assert.AreEqual(1, scheduler.GetPausedTriggerGroups().Count); Thread.Sleep(TimeSpan.FromSeconds(3)); scheduler.ResumeTriggers(GroupMatcher<TriggerKey>.GroupEquals(schedId)); Assert.IsNotNull(scheduler.GetTrigger(new TriggerKey("trig_2", schedId))); Assert.IsNotNull(scheduler.GetJobDetail(new JobKey("job_1", schedId))); Assert.IsNotNull(scheduler.GetMetaData()); Assert.IsNotNull(scheduler.GetCalendar("weeklyCalendar")); Thread.Sleep(TimeSpan.FromSeconds(20)); scheduler.Standby(); CollectionAssert.IsNotEmpty(scheduler.GetCalendarNames()); CollectionAssert.IsNotEmpty(scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(schedId))); CollectionAssert.IsNotEmpty(scheduler.GetTriggersOfJob(new JobKey("job_2", schedId))); Assert.IsNotNull(scheduler.GetJobDetail(new JobKey("job_2", schedId))); scheduler.DeleteCalendar("cronCalendar"); scheduler.DeleteCalendar("holidayCalendar"); scheduler.DeleteJob(new JobKey("lonelyJob", "lonelyGroup")); scheduler.DeleteJob(job.Key); scheduler.GetJobGroupNames(); scheduler.GetCalendarNames(); scheduler.GetTriggerGroupNames(); } } finally { scheduler.Shutdown(false); } }
public void TestScheduleMultipleTriggersForAJob() { IJobDetail job = JobBuilder.Create<TestJob>().WithIdentity("job1", "group1").Build(); ITrigger trigger1 = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).RepeatForever()) .Build(); ITrigger trigger2 = TriggerBuilder.Create() .WithIdentity("trigger2", "group1") .StartNow() .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).RepeatForever()) .Build(); Collection.ISet<ITrigger> triggersForJob = new Collection.HashSet<ITrigger>(); triggersForJob.Add(trigger1); triggersForJob.Add(trigger2); IScheduler sched = CreateScheduler("testScheduleMultipleTriggersForAJob", 5); sched.ScheduleJob(job, triggersForJob, true); IList<ITrigger> triggersOfJob = sched.GetTriggersOfJob(job.Key); Assert.That(triggersOfJob.Count, Is.EqualTo(2)); Assert.That(triggersOfJob.Contains(trigger1)); Assert.That(triggersOfJob.Contains(trigger2)); sched.Shutdown(false); }
/// <summary> /// Resume (un-pause) all of the <see cref="ITrigger" />s /// in the given group. /// <p> /// If any <see cref="ITrigger" /> missed one or more fire-times, then the /// <see cref="ITrigger" />'s misfire instruction will be applied. /// </p> /// </summary> public IList<string> ResumeTriggers(GroupMatcher<TriggerKey> matcher) { try { DeletePausedTriggerGroup(matcher); Collection.HashSet<string> groups = new Collection.HashSet<string>(); Collection.ISet<TriggerKey> keys = GetTriggerKeys(matcher); foreach (TriggerKey key in keys) { ResumeTrigger(key); groups.Add(key.Group); } return new List<string>(groups); } catch (Exception e) { throw new JobPersistenceException("Couldn't resume trigger group '" + matcher + "': " + e.Message, e); } }
/// <summary> /// Resume (un-pause) all of the <see cref="IJob" />s in /// the given group. /// <p> /// If any of the <see cref="IJob" /> s had <see cref="ITrigger" /> s that /// missed one or more fire-times, then the <see cref="ITrigger" />'s /// misfire instruction will be applied. /// </p> /// </summary> public Collection.ISet<string> ResumeJobs(GroupMatcher<JobKey> matcher) { Collection.ISet<String> groupNames = new Collection.HashSet<string>(); Collection.ISet<JobKey> jobKeys = GetJobKeys(matcher); foreach (JobKey jobKey in jobKeys) { ResumeJob(jobKey); groupNames.Add(jobKey.Group); } return groupNames; }
/// <summary> /// Pause all of the <see cref="IJob" />s in the given /// group - by pausing all of their <see cref="ITrigger" />s. /// <p> /// The JobStore should "remember" that the group is paused, and impose the /// pause on any new jobs that are added to the group while the group is /// paused. /// </p> /// </summary> /// <seealso cref="string"> /// </seealso> public IList<string> PauseJobs(GroupMatcher<JobKey> matcher) { Collection.ISet<string> groupNames = new Collection.HashSet<string>(); Collection.ISet<JobKey> jobKeys = GetJobKeys(matcher); foreach (JobKey jobKey in jobKeys) { PauseJob(jobKey); groupNames.Add(jobKey.Group); } return new List<string>(groupNames); }