Example #1
0
        public void Schedule(int startDay, int endDay, int periodDays, int jobExecutionCount,
                             TriggerScheduleAction misfireAction,
                             TriggerScheduleCondition condition, int timeBasisDay, bool isFirstTime,
                             TriggerScheduleAction expectedAction, int expectedFireDay,
                             int expectedJobExecutionCountRemaining, bool expectedIsActive, bool expectedIsFirstTime)
        {
            DateTime startTime            = new DateTime(1970, 1, startDay);
            DateTime?endTime              = endDay != 0 ? new DateTime(1970, 1, endDay) : (DateTime?)null;
            TimeSpan?period               = periodDays != 0 ? new TimeSpan(periodDays, 0, 0, 0) : (TimeSpan?)null;
            int?     jobExecutionCountArg = jobExecutionCount >= 0 ? jobExecutionCount : (int?)null;
            DateTime timeBasis            = new DateTime(1970, 1, timeBasisDay);
            DateTime?expectedFireTime     = expectedFireDay != 0
                                                        ? new DateTime(1970, 1, expectedFireDay, 0, 0, 0, DateTimeKind.Utc)
                                                        : (DateTime?)null;
            int?expectedJobExecutionCountRemainingArg = expectedJobExecutionCountRemaining >= 0
                                                                        ? expectedJobExecutionCountRemaining
                                                                        : (int?)null;

            PeriodicTrigger trigger = new PeriodicTrigger(startTime, endTime, period, jobExecutionCountArg);

            trigger.MisfireAction    = misfireAction;
            trigger.MisfireThreshold = TimeSpan.Zero;
            trigger.IsFirstTime      = expectedIsFirstTime;

            TriggerScheduleAction action = trigger.Schedule(condition, timeBasis, null);

            Assert.AreEqual(expectedAction, action);
            DateTimeAssert.AreEqualIncludingKind(expectedFireTime, trigger.NextFireTimeUtc);
            Assert.AreEqual(expectedJobExecutionCountRemainingArg, trigger.JobExecutionCountRemaining);
            Assert.AreEqual(expectedIsActive, trigger.IsActive);
        }
        private void ScheduleJob(JobDetails jobDetails)
        {
            DateTime timeBasis = DateTime.UtcNow;

            TriggerScheduleAction action = UpdateTrigger(timeBasis, jobDetails);

            PerformActionAndSaveChanges(timeBasis, jobDetails, action);
        }
		/// <summary>
		/// Creates a periodic trigger.
		/// </summary>
		/// <param name="startTimeUtc">The UTC date and time when the trigger will first fire</param>
		/// <param name="endTimeUtc">The UTC date and time when the trigger must stop firing.
		/// If the time is set to null, the trigger may continue firing indefinitely.</param>
		/// <param name="period">The recurrence period of the trigger.
		/// If the period is set to null, the trigger will fire exactly once
		/// and never recur.</param>
		/// <param name="jobExecutionCount">The number of job executions remaining before the trigger
		/// stops firing.  This number is decremented each time the job executes
		/// until it reaches zero.  If the count is set to null, the number of times the job
		/// may execute is unlimited.</param>
		/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="period"/> is negative or zero</exception>
		/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="jobExecutionCount"/> is negative</exception>
		public PeriodicTrigger(DateTime startTimeUtc, DateTime? endTimeUtc, TimeSpan? period, int? jobExecutionCount)
		{
			if (period.HasValue && period.Value.Ticks <= 0)
				throw new ArgumentOutOfRangeException("period", "The recurrence period must not be negative or zero.");
			if (jobExecutionCount.HasValue && jobExecutionCount.Value < 0)
				throw new ArgumentOutOfRangeException("jobExecutionCount", "The job execution count remaining must not be negative.");

			this.startTimeUtc = DateTimeUtils.AssumeUniversalTime(startTimeUtc);
			this.endTimeUtc = DateTimeUtils.AssumeUniversalTime(endTimeUtc);
			this.period = period;
			jobExecutionCountRemaining = jobExecutionCount;
			isFirstTime = true;

			misfireAction = DefaultMisfireAction;
		}
Example #4
0
        public void Schedule(string utcFireTime, TriggerScheduleAction expectedAction, int expectedRemainingJobCount)
        {
            var      startDate         = new DateTime(2000, 3, 14);
            DateTime?endDate           = new DateTime(2001, 3, 14);
            var      dailyFireWindow   = new DailyFireWindow(3, 16);
            TimeSpan?interval          = new TimeSpan(0, 1, 0);
            int?     jobExecutionCount = 33;

            var trigger = new DailyPeriodicWindowTrigger(startDate, endDate, dailyFireWindow, interval, jobExecutionCount);

            var triggerScheduleAction = trigger.Schedule(TriggerScheduleCondition.Fire, DateTime.Parse(utcFireTime), null);

            Assert.AreEqual(expectedAction, triggerScheduleAction);
            Assert.AreEqual(expectedRemainingJobCount, trigger.JobExecutionCountRemaining);
        }
        /// <summary>
        /// Creates a periodic trigger.
        /// </summary>
        /// <param name="startTimeUtc">The UTC date and time when the trigger will first fire</param>
        /// <param name="endTimeUtc">The UTC date and time when the trigger must stop firing.
        /// If the time is set to null, the trigger may continue firing indefinitely.</param>
        /// <param name="period">The recurrence period of the trigger.
        /// If the period is set to null, the trigger will fire exactly once
        /// and never recur.</param>
        /// <param name="jobExecutionCount">The number of job executions remaining before the trigger
        /// stops firing.  This number is decremented each time the job executes
        /// until it reaches zero.  If the count is set to null, the number of times the job
        /// may execute is unlimited.</param>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="period"/> is negative or zero</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="jobExecutionCount"/> is negative</exception>
        public PeriodicTrigger(DateTime startTimeUtc, DateTime?endTimeUtc, TimeSpan?period, int?jobExecutionCount)
        {
            if (period.HasValue && period.Value.Ticks <= 0)
            {
                throw new ArgumentOutOfRangeException("period", "The recurrence period must not be negative or zero.");
            }
            if (jobExecutionCount.HasValue && jobExecutionCount.Value < 0)
            {
                throw new ArgumentOutOfRangeException("jobExecutionCount", "The job execution count remaining must not be negative.");
            }

            this.startTimeUtc          = DateTimeUtils.AssumeUniversalTime(startTimeUtc);
            this.endTimeUtc            = DateTimeUtils.AssumeUniversalTime(endTimeUtc);
            this.period                = period;
            jobExecutionCountRemaining = jobExecutionCount;
            isFirstTime                = true;

            misfireAction = DefaultMisfireAction;
        }
        internal override TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc)
        {
            if (action != TriggerScheduleAction.ExecuteJob)
            {
                return(base.ScheduleSuggestedAction(action, timeBasisUtc));
            }

            if (CannotExecuteAgain() || EndTimeHasPassed(timeBasisUtc))
            {
                return(StopAction());
            }

            if (!StartTimeHasPassed(timeBasisUtc) || !InDailyFireWindow(timeBasisUtc))
            {
                return(SkipAction());
            }

            return(ExecuteAction());
        }
        private void PerformActionAndSaveChanges(DateTime timeBasis, JobDetails jobDetails, TriggerScheduleAction action)
        {
            try
            {
                switch (action)
                {
                case TriggerScheduleAction.Skip:
                    jobDetails.JobState = JobState.Scheduled;
                    jobStore.SaveJobDetails(jobDetails);
                    return;

                case TriggerScheduleAction.DeleteJob:
                    jobStore.DeleteJob(jobDetails.JobSpec.Name);
                    return;

                case TriggerScheduleAction.ExecuteJob:
                    jobDetails.JobState = JobState.Running;
                    jobDetails.LastJobExecutionDetails = new JobExecutionDetails(guid, timeBasis);
                    jobDetails.LastJobExecutionDetails.StatusMessage = "Running.";
                    jobStore.SaveJobDetails(jobDetails);

                    BeginExecuteJob(jobDetails);
                    return;

                case TriggerScheduleAction.Stop:
                    break;

                default:
                    logger.WarnFormat("Trigger for job '{0}' returned an unexpected action '{1}'.  The trigger will be stopped.",
                                      jobDetails.JobSpec.Name, action);
                    break;
                }

                jobDetails.JobState = JobState.Stopped;
                jobDetails.NextTriggerFireTimeUtc      = null;
                jobDetails.NextTriggerMisfireThreshold = null;
                jobStore.SaveJobDetails(jobDetails);
            }
            catch (ConcurrentModificationException ex)
            {
                logger.DebugFormat(ex,
                                   "Job '{0}' was concurrently modified by another scheduler instance so the changes made "
                                   + "by this scheduler instance will be discarded.  This is normal behavior in a cluster.",
                                   jobDetails.JobSpec.Name);
            }
        }
Example #8
0
        [TestCase((TriggerScheduleAction)9999)]          // invalid trigger action
        public void ScheduleHandlesUnexpectedActionReceivedFromTriggerByStoppingTheTrigger(TriggerScheduleAction action)
        {
            JobDetails jobDetails = new JobDetails(dummyJobSpec, DateTime.UtcNow);

            jobDetails.JobState = JobState.Pending;

            PrepareMockJobWatcher(jobDetails);

            Expect.Call(mockTrigger.Schedule(TriggerScheduleCondition.Latch, DateTime.UtcNow, null))
            .Constraints(Rhino.Mocks.Constraints.Is.Equal(TriggerScheduleCondition.Latch), Rhino.Mocks.Constraints.Is.Anything(), Rhino.Mocks.Constraints.Is.Null())
            .Return(action);
            Expect.Call(mockTrigger.NextFireTimeUtc).Return(new DateTime(1970, 1, 5, 0, 0, 0, DateTimeKind.Utc));
            Expect.Call(mockTrigger.NextMisfireThreshold).Return(new TimeSpan(0, 1, 0));

            mockJobStore.SaveJobDetails(jobDetails);
            LastCall.Do((SaveJobDetailsDelegate)WakeOnSaveJobDetails);

            Mocks.ReplayAll();

            RunSchedulerUntilWake();

            Assert.AreEqual(JobState.Stopped, jobDetails.JobState);
            Assert.IsNull(jobDetails.NextTriggerFireTimeUtc);
            Assert.IsNull(jobDetails.NextTriggerMisfireThreshold);
            Assert.IsNull(jobDetails.LastJobExecutionDetails);
            Assert.IsNull(jobDetails.JobSpec.JobData);
        }
Example #9
0
        public void ScheduleHandlesUnexpectedActionReceivedFromTriggerByStoppingTheTrigger(TriggerScheduleAction action)
        {
            JobDetails jobDetails = new JobDetails(dummyJobSpec, DateTime.UtcNow);
            jobDetails.JobState = JobState.Pending;

            PrepareMockJobWatcher(jobDetails);

            Expect.Call(mockTrigger.Schedule(TriggerScheduleCondition.Latch, DateTime.UtcNow, null))
                .Constraints(Rhino.Mocks.Constraints.Is.Equal(TriggerScheduleCondition.Latch), Rhino.Mocks.Constraints.Is.Anything(), Rhino.Mocks.Constraints.Is.Null())
                .Return(action);
            Expect.Call(mockTrigger.NextFireTimeUtc).Return(new DateTime(1970, 1, 5, 0, 0, 0, DateTimeKind.Utc));
            Expect.Call(mockTrigger.NextMisfireThreshold).Return(new TimeSpan(0, 1, 0));

            mockJobStore.SaveJobDetails(jobDetails);
            LastCall.Do((SaveJobDetailsDelegate) WakeOnSaveJobDetails);

            Mocks.ReplayAll();

            RunSchedulerUntilWake();

            Assert.AreEqual(JobState.Stopped, jobDetails.JobState);
            Assert.IsNull(jobDetails.NextTriggerFireTimeUtc);
            Assert.IsNull(jobDetails.NextTriggerMisfireThreshold);
            Assert.IsNull(jobDetails.LastJobExecutionDetails);
            Assert.IsNull(jobDetails.JobSpec.JobData);
        }
        private TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc)
        {
            switch (action)
            {
            case TriggerScheduleAction.Stop:
                break;

            case TriggerScheduleAction.ExecuteJob:
                // If the job cannot execute again then stop.
                if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0)
                {
                    break;
                }

                // If the end time has passed then stop.
                if (endTimeUtc.HasValue && timeBasisUtc > endTimeUtc.Value)
                {
                    break;
                }

                // If the start time is still in the future then hold off until then.
                if (timeBasisUtc < startTimeUtc)
                {
                    nextFireTimeUtc = startTimeUtc;
                    return(TriggerScheduleAction.Skip);
                }

                // Otherwise execute the job.
                nextFireTimeUtc             = null;
                jobExecutionCountRemaining -= 1;
                return(TriggerScheduleAction.ExecuteJob);

            case TriggerScheduleAction.DeleteJob:
                nextFireTimeUtc            = null;
                jobExecutionCountRemaining = 0;
                return(TriggerScheduleAction.DeleteJob);

            case TriggerScheduleAction.Skip:
                // If the job cannot execute again then stop.
                if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0)
                {
                    break;
                }

                // If the start time is still in the future then hold off until then.
                if (isFirstTime || timeBasisUtc < startTimeUtc)
                {
                    nextFireTimeUtc = startTimeUtc;
                    return(TriggerScheduleAction.Skip);
                }

                // If the trigger is not periodic then we must be skipping the only chance the
                // job had to run so stop the trigger.
                if (!period.HasValue)
                {
                    break;
                }

                // Compute when the next occurrence should be.
                TimeSpan timeSinceStart      = timeBasisUtc - startTimeUtc;
                TimeSpan timeSinceLastPeriod = new TimeSpan(timeSinceStart.Ticks % period.Value.Ticks);
                nextFireTimeUtc = timeBasisUtc + period - timeSinceLastPeriod;

                // If the next occurrence is past the end time then stop.
                if (nextFireTimeUtc > endTimeUtc)
                {
                    break;
                }

                // Otherwise we're good.
                return(TriggerScheduleAction.Skip);
            }

            // Stop the trigger.
            nextFireTimeUtc            = null;
            jobExecutionCountRemaining = 0;
            return(TriggerScheduleAction.Stop);
        }
		private TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc)
		{
			switch (action)
			{
				case TriggerScheduleAction.Stop:
					break;

				case TriggerScheduleAction.ExecuteJob:
					// If the job cannot execute again then stop.
					if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0)
						break;

					// If the end time has passed then stop.
					if (endTimeUtc.HasValue && timeBasisUtc > endTimeUtc.Value)
						break;

					// If the start time is still in the future then hold off until then.
					if (timeBasisUtc < startTimeUtc)
					{
						nextFireTimeUtc = startTimeUtc;
						return TriggerScheduleAction.Skip;
					}

					// Otherwise execute the job.
					nextFireTimeUtc = null;
					jobExecutionCountRemaining -= 1;
					return TriggerScheduleAction.ExecuteJob;

				case TriggerScheduleAction.DeleteJob:
					nextFireTimeUtc = null;
					jobExecutionCountRemaining = 0;
					return TriggerScheduleAction.DeleteJob;

				case TriggerScheduleAction.Skip:
					// If the job cannot execute again then stop.
					if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0)
						break;

					// If the start time is still in the future then hold off until then.
					if (isFirstTime || timeBasisUtc < startTimeUtc)
					{
						nextFireTimeUtc = startTimeUtc;
						return TriggerScheduleAction.Skip;
					}

					// If the trigger is not periodic then we must be skipping the only chance the
					// job had to run so stop the trigger.
					if (! period.HasValue)
						break;

					// Compute when the next occurrence should be.
					TimeSpan timeSinceStart = timeBasisUtc - startTimeUtc;
					TimeSpan timeSinceLastPeriod = new TimeSpan(timeSinceStart.Ticks%period.Value.Ticks);
					nextFireTimeUtc = timeBasisUtc + period - timeSinceLastPeriod;

					// If the next occurrence is past the end time then stop.
					if (nextFireTimeUtc > endTimeUtc)
						break;

					// Otherwise we're good.
					return TriggerScheduleAction.Skip;
			}

			// Stop the trigger.
			nextFireTimeUtc = null;
			jobExecutionCountRemaining = 0;
			return TriggerScheduleAction.Stop;
		}
Example #12
0
        public void Schedule(int startDay, int endDay, int periodDays, int jobExecutionCount,
            TriggerScheduleAction misfireAction,
            TriggerScheduleCondition condition, int timeBasisDay, bool isFirstTime,
            TriggerScheduleAction expectedAction, int expectedFireDay,
            int expectedJobExecutionCountRemaining, bool expectedIsActive, bool expectedIsFirstTime)
        {
            DateTime startTime = new DateTime(1970, 1, startDay);
            DateTime? endTime = endDay != 0 ? new DateTime(1970, 1, endDay) : (DateTime?) null;
            TimeSpan? period = periodDays != 0 ? new TimeSpan(periodDays, 0, 0, 0) : (TimeSpan?) null;
            int? jobExecutionCountArg = jobExecutionCount >= 0 ? jobExecutionCount : (int?) null;
            DateTime timeBasis = new DateTime(1970, 1, timeBasisDay);
            DateTime? expectedFireTime = expectedFireDay != 0
                                         	? new DateTime(1970, 1, expectedFireDay, 0, 0, 0, DateTimeKind.Utc)
                                         	: (DateTime?) null;
            int? expectedJobExecutionCountRemainingArg = expectedJobExecutionCountRemaining >= 0
                                                         	? expectedJobExecutionCountRemaining
                                                         	: (int?) null;

            PeriodicTrigger trigger = new PeriodicTrigger(startTime, endTime, period, jobExecutionCountArg);
            trigger.MisfireAction = misfireAction;
            trigger.MisfireThreshold = TimeSpan.Zero;
            trigger.IsFirstTime = expectedIsFirstTime;

            TriggerScheduleAction action = trigger.Schedule(condition, timeBasis, null);
            Assert.AreEqual(expectedAction, action);
            DateTimeAssert.AreEqualIncludingKind(expectedFireTime, trigger.NextFireTimeUtc);
            Assert.AreEqual(expectedJobExecutionCountRemainingArg, trigger.JobExecutionCountRemaining);
            Assert.AreEqual(expectedIsActive, trigger.IsActive);
        }
        internal override TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc)
        {
            if (action != TriggerScheduleAction.ExecuteJob)
                return base.ScheduleSuggestedAction(action, timeBasisUtc);
            
            if (CannotExecuteAgain() || EndTimeHasPassed(timeBasisUtc))
                return StopAction();

            if (!StartTimeHasPassed(timeBasisUtc) || !InDailyFireWindow(timeBasisUtc))
                return SkipAction();

            return ExecuteAction();
        }
		private void PerformActionAndSaveChanges(DateTime timeBasis, JobDetails jobDetails, TriggerScheduleAction action)
		{
			try
			{
				switch (action)
				{
					case TriggerScheduleAction.Skip:
						jobDetails.JobState = JobState.Scheduled;
						jobStore.SaveJobDetails(jobDetails);
						return;

					case TriggerScheduleAction.DeleteJob:
						jobStore.DeleteJob(jobDetails.JobSpec.Name);
						return;

					case TriggerScheduleAction.ExecuteJob:
						jobDetails.JobState = JobState.Running;
						jobDetails.LastJobExecutionDetails = new JobExecutionDetails(guid, timeBasis);
						jobDetails.LastJobExecutionDetails.StatusMessage = "Running.";
						jobStore.SaveJobDetails(jobDetails);

						BeginExecuteJob(jobDetails);
						return;

					case TriggerScheduleAction.Stop:
						break;

					default:
						logger.WarnFormat("Trigger for job '{0}' returned an unexpected action '{1}'.  The trigger will be stopped.",
						                  jobDetails.JobSpec.Name, action);
						break;
				}

				jobDetails.JobState = JobState.Stopped;
				jobDetails.NextTriggerFireTimeUtc = null;
				jobDetails.NextTriggerMisfireThreshold = null;
				jobStore.SaveJobDetails(jobDetails);
			}
			catch (ConcurrentModificationException ex)
			{
				logger.DebugFormat(ex,
				                   "Job '{0}' was concurrently modified by another scheduler instance so the changes made "
				                   + "by this scheduler instance will be discarded.  This is normal behavior in a cluster.",
				                   jobDetails.JobSpec.Name);
			}
		}
        public void Schedule(string utcFireTime, TriggerScheduleAction expectedAction, int expectedRemainingJobCount)
        {
            var startDate = new DateTime(2000, 3, 14);
            DateTime? endDate = new DateTime(2001, 3, 14);
            var dailyFireWindow = new DailyFireWindow(3, 16);
            TimeSpan? interval = new TimeSpan(0, 1, 0);
            int? jobExecutionCount = 33;

            var trigger = new DailyPeriodicWindowTrigger(startDate, endDate, dailyFireWindow, interval, jobExecutionCount);

            var triggerScheduleAction = trigger.Schedule(TriggerScheduleCondition.Fire, DateTime.Parse(utcFireTime), null);

            Assert.AreEqual(expectedAction, triggerScheduleAction);
            Assert.AreEqual(expectedRemainingJobCount, trigger.JobExecutionCountRemaining);
        }