Exemple #1
0
        /// <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>
        public static IReadOnlyList <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());
        }
Exemple #2
0
        /// <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);
        }
Exemple #3
0
        /// <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());
        }
Exemple #4
0
        // 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);
        }
Exemple #5
0
        [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);
        }
        /// <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);
                }
            }
        }
        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);
            }
        }
 internal static void PopulateBaseTriggerProperties(IOperableTrigger quartzTrigger, DbAbstractTrigger dbTrigger, string instanceName)
 {
     dbTrigger.CalendarName = quartzTrigger.CalendarName;
     dbTrigger.Description = quartzTrigger.Description;
     dbTrigger.JobGroup = quartzTrigger.JobKey.Group;
     dbTrigger.JobName = quartzTrigger.JobKey.Name;
     dbTrigger.MisfireInstruction = quartzTrigger.MisfireInstruction;
     dbTrigger.Priority = quartzTrigger.Priority;
     dbTrigger.Scheduler = instanceName;
     if (quartzTrigger.JobDataMap != null)
     {
         dbTrigger.JobData = new List<DbJobData>();
         foreach (var jobDataItem in quartzTrigger.JobDataMap)
         {
             DbJobData dbJobData = new DbJobData();
             dbJobData.Key = jobDataItem.Key;
             dbJobData.Value = jobDataItem.Value;
             dbJobData.Type = GetStorableJobTypeName(jobDataItem.Value.GetType());
             dbTrigger.JobData.Add(dbJobData);
         }
     }
     dbTrigger.StartTime = new DateEpoch { Date = quartzTrigger.StartTimeUtc.UtcDateTime };
     var endTime = quartzTrigger.EndTimeUtc;
     if (endTime.HasValue)
         dbTrigger.EndTime = new DateEpoch { Date = endTime.Value.UtcDateTime };
     var nextFireTime = quartzTrigger.GetNextFireTimeUtc();
     if (nextFireTime.HasValue)
         dbTrigger.NextFireTime = new DateEpoch { Date = nextFireTime.Value.UtcDateTime };
     var previousFireTime = quartzTrigger.GetPreviousFireTimeUtc();
     if (previousFireTime.HasValue)
         dbTrigger.PreviousFireTime = new DateEpoch { Date = previousFireTime.Value.UtcDateTime };
 }
        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);
            }
        }
        /// <summary>
        /// Inform the <see cref="T:Quartz.Spi.IJobStore"/> that the scheduler has completed the
        ///             firing of the given <see cref="T:Quartz.ITrigger"/> (and the execution its
        ///             associated <see cref="T:Quartz.IJob"/>), and that the <see cref="T:Quartz.JobDataMap"/>
        ///             in the given <see cref="T:Quartz.IJobDetail"/> should be updated if the <see cref="T:Quartz.IJob"/>
        ///             is stateful.
        /// </summary>
        public override void TriggeredJobComplete(IOperableTrigger trigger, IJobDetail jobDetail, SchedulerInstruction triggerInstCode)
        {
            var jobHashKey = this.RedisJobStoreSchema.JobHashKey(jobDetail.Key);

            var jobDataMapHashKey = this.RedisJobStoreSchema.JobDataMapHashKey(jobDetail.Key);

            var triggerHashKey = this.RedisJobStoreSchema.TriggerHashkey(trigger.Key);

            if (this.Db.KeyExists(jobHashKey))
            {
                Logger.InfoFormat("{0} - Job has completed", jobHashKey);

                if (jobDetail.PersistJobDataAfterExecution)
                {
                    var jobDataMap = jobDetail.JobDataMap;

                    Db.KeyDelete(jobDataMapHashKey);
                    if (jobDataMap != null && !jobDataMap.IsEmpty)
                    {
                        Db.HashSet(jobDataMapHashKey, ConvertToHashEntries(jobDataMap));
                    }

                }

                if (jobDetail.ConcurrentExecutionDisallowed)
                {

                    Db.SetRemove(this.RedisJobStoreSchema.BlockedJobsSet(), jobHashKey);

                    Db.KeyDelete(this.RedisJobStoreSchema.JobBlockedKey(jobDetail.Key));

                    var jobTriggersSetKey = this.RedisJobStoreSchema.JobTriggersSetKey(jobDetail.Key);

                    foreach (var nonConcurrentTriggerHashKey in this.Db.SetMembers(jobTriggersSetKey))
                    {
                        var score =
                            this.Db.SortedSetScore(this.RedisJobStoreSchema.TriggerStateSetKey(RedisTriggerState.Blocked),
                                                    nonConcurrentTriggerHashKey);
                        if (score.HasValue)
                        {
                            this.SetTriggerState(RedisTriggerState.Paused, score.Value, nonConcurrentTriggerHashKey);
                        }
                        else
                        {
                            score =
                                this.Db.SortedSetScoreAsync(
                                    this.RedisJobStoreSchema.TriggerStateSetKey(RedisTriggerState.PausedBlocked),
                                    nonConcurrentTriggerHashKey).Result;

                            if (score.HasValue)
                            {
                                this.SetTriggerState(RedisTriggerState.Paused, score.Value, nonConcurrentTriggerHashKey);
                            }
                        }
                    }
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }

            }
            else
            {
                this.Db.SetRemove(this.RedisJobStoreSchema.BlockedJobsSet(), jobHashKey);
            }

            if (this.Db.KeyExists(triggerHashKey))
            {
                if (triggerInstCode == SchedulerInstruction.DeleteTrigger)
                {
                    if (trigger.GetNextFireTimeUtc().HasValue == false)
                    {
                        if (string.IsNullOrEmpty(this.Db.HashGet(triggerHashKey, RedisJobStoreSchema.NextFireTime)))
                        {
                            RemoveTrigger(trigger.Key);
                        }
                    }
                    else
                    {
                        this.RemoveTrigger(trigger.Key);
                        this.SchedulerSignaler.SignalSchedulingChange(null);
                    }
                }
                else if (triggerInstCode == SchedulerInstruction.SetTriggerComplete)
                {
                    this.SetTriggerState(RedisTriggerState.Completed, DateTimeOffset.UtcNow.DateTime.ToUnixTimeMilliSeconds(), triggerHashKey);
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
                else if (triggerInstCode == SchedulerInstruction.SetTriggerError)
                {
                    double score = trigger.GetNextFireTimeUtc().HasValue
                                       ? trigger.GetNextFireTimeUtc().Value.DateTime.ToUnixTimeMilliSeconds() : 0;
                    this.SetTriggerState(RedisTriggerState.Error, score, triggerHashKey);
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
                else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersError)
                {
                    var jobTriggersSetKey = this.RedisJobStoreSchema.JobTriggersSetKey(jobDetail.Key);

                    foreach (var errorTriggerHashKey in this.Db.SetMembersAsync(jobTriggersSetKey).Result)
                    {
                        var nextFireTime = this.Db.HashGetAsync(errorTriggerHashKey.ToString(), RedisJobStoreSchema.NextFireTime).Result;
                        var score = string.IsNullOrEmpty(nextFireTime) ? 0 : double.Parse(nextFireTime);
                        this.SetTriggerState(RedisTriggerState.Error, score, errorTriggerHashKey);
                    }
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
                else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersComplete)
                {
                    var jobTriggerSetKey = this.RedisJobStoreSchema.JobTriggersSetKey(jobDetail.Key);

                    foreach (var completedTriggerHashKey in this.Db.SetMembersAsync(jobTriggerSetKey).Result)
                    {
                        this.SetTriggerState(RedisTriggerState.Completed, DateTimeOffset.UtcNow.DateTime.ToUnixTimeMilliSeconds(),
                                             completedTriggerHashKey);
                    }

                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
            }
        }
Exemple #11
0
        public Trigger(IOperableTrigger newTrigger, string schedulerInstanceName)
        {
            if (newTrigger == null)
            {
                return;
            }

            Name  = newTrigger.Key.Name;
            Group = newTrigger.Key.Group;

            JobName   = newTrigger.JobKey.Name;
            JobKey    = $"{JobName}/{newTrigger.JobKey.Group}";
            Scheduler = schedulerInstanceName;

            State                   = InternalTriggerState.Waiting;
            Description             = newTrigger.Description;
            CalendarName            = newTrigger.CalendarName;
            JobDataMap              = newTrigger.JobDataMap.WrappedMap;
            FinalFireTimeUtc        = newTrigger.FinalFireTimeUtc;
            MisfireInstruction      = newTrigger.MisfireInstruction;
            Priority                = newTrigger.Priority;
            HasMillisecondPrecision = newTrigger.HasMillisecondPrecision;
            FireInstanceId          = newTrigger.FireInstanceId;
            EndTimeUtc              = newTrigger.EndTimeUtc;
            StartTimeUtc            = newTrigger.StartTimeUtc;
            NextFireTimeUtc         = newTrigger.GetNextFireTimeUtc();
            PreviousFireTimeUtc     = newTrigger.GetPreviousFireTimeUtc();

            if (NextFireTimeUtc != null)
            {
                NextFireTimeTicks = NextFireTimeUtc.Value.UtcTicks;
            }

            // Init trigger specific properties according to type of newTrigger.
            // If an option doesn't apply to the type of trigger it will stay null by default.

            switch (newTrigger)
            {
            case CronTriggerImpl cronTriggerImpl:
                Cron = new CronOptions
                {
                    CronExpression = cronTriggerImpl.CronExpressionString,
                    TimeZoneId     = cronTriggerImpl.TimeZone.Id
                };
                return;

            case SimpleTriggerImpl simpTriggerImpl:
                Simp = new SimpleOptions
                {
                    RepeatCount    = simpTriggerImpl.RepeatCount,
                    RepeatInterval = simpTriggerImpl.RepeatInterval
                };
                return;

            case CalendarIntervalTriggerImpl calTriggerImpl:
                Cal = new CalendarOptions
                {
                    RepeatIntervalUnit = calTriggerImpl.RepeatIntervalUnit,
                    RepeatInterval     = calTriggerImpl.RepeatInterval,
                    TimesTriggered     = calTriggerImpl.TimesTriggered,
                    TimeZoneId         = calTriggerImpl.TimeZone.Id,
                    PreserveHourOfDayAcrossDaylightSavings = calTriggerImpl.PreserveHourOfDayAcrossDaylightSavings,
                    SkipDayIfHourDoesNotExist = calTriggerImpl.SkipDayIfHourDoesNotExist
                };
                return;

            case DailyTimeIntervalTriggerImpl dayTriggerImpl:
                Day = new DailyTimeOptions
                {
                    RepeatCount        = dayTriggerImpl.RepeatCount,
                    RepeatIntervalUnit = dayTriggerImpl.RepeatIntervalUnit,
                    RepeatInterval     = dayTriggerImpl.RepeatInterval,
                    StartTimeOfDay     = dayTriggerImpl.StartTimeOfDay,
                    EndTimeOfDay       = dayTriggerImpl.EndTimeOfDay,
                    DaysOfWeek         = dayTriggerImpl.DaysOfWeek,
                    TimesTriggered     = dayTriggerImpl.TimesTriggered,
                    TimeZoneId         = dayTriggerImpl.TimeZone.Id
                };
                break;
            }
        }
        /// <summary>
        ///  Determine whether or not the given trigger has misfired.If so, notify {SchedulerSignaler} and update the trigger.
        /// </summary>
        /// <param name="trigger">IOperableTrigger</param>
        /// <returns>applied or not</returns>
        protected bool ApplyMisfire(IOperableTrigger trigger)
        {
            double misfireTime = DateTimeOffset.UtcNow.DateTime.ToUnixTimeMilliSeconds();
            double score = misfireTime;

            if (MisfireThreshold > 0)
            {
                misfireTime = misfireTime - MisfireThreshold;
            }

            //if the trigger has no next fire time or exceeds the misfirethreshold or enable ignore misfirepolicy
            // then dont apply misfire.
            DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();

            if (nextFireTime.HasValue == false ||
               (nextFireTime.HasValue && nextFireTime.Value.DateTime.ToUnixTimeMilliSeconds() > misfireTime) ||
               trigger.MisfireInstruction == -1)
            {
                return false;
            }

            ICalendar calendar = null;

            if (!string.IsNullOrEmpty(trigger.CalendarName))
            {
                calendar = RetrieveCalendar(trigger.CalendarName);
            }

            SchedulerSignaler.NotifyTriggerListenersMisfired((IOperableTrigger)trigger.Clone());

            trigger.UpdateAfterMisfire(calendar);

            StoreTrigger(trigger, true);

            if (nextFireTime.HasValue == false)
            {
                SetTriggerState(RedisTriggerState.Completed,
                                     score, RedisJobStoreSchema.TriggerHashkey(trigger.Key));
                SchedulerSignaler.NotifySchedulerListenersFinalized(trigger);
            }
            else if (nextFireTime.Equals(trigger.GetNextFireTimeUtc()))
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// Inform the <see cref="T:Quartz.Spi.IJobStore"/> that the scheduler no longer plans to
        ///             fire the given <see cref="T:Quartz.ITrigger"/>, that it had previously acquired
        ///             (reserved).
        /// </summary>
        public void ReleaseAcquiredTrigger(IOperableTrigger trigger)
        {
            var triggerHashKey = RedisJobStoreSchema.TriggerHashkey(trigger.Key);

            var score =
                Db.SortedSetScore(RedisJobStoreSchema.TriggerStateSetKey(RedisTriggerState.Acquired),
                                        triggerHashKey);

            if (score.HasValue)
            {
                if (trigger.GetNextFireTimeUtc().HasValue)
                {
                    SetTriggerState(RedisTriggerState.Waiting,
                                         trigger.GetNextFireTimeUtc().Value.DateTime.ToUnixTimeMilliSeconds(), triggerHashKey);
                }
                else
                {
                    this.UnsetTriggerState(triggerHashKey);
                }
            }
        }
Exemple #14
0
        /// <summary>
        /// Inform the <see cref="T:Quartz.Spi.IJobStore"/> that the scheduler has completed the
        ///             firing of the given <see cref="T:Quartz.ITrigger"/> (and the execution its
        ///             associated <see cref="T:Quartz.IJob"/>), and that the <see cref="T:Quartz.JobDataMap"/>
        ///             in the given <see cref="T:Quartz.IJobDetail"/> should be updated if the <see cref="T:Quartz.IJob"/>
        ///             is stateful.
        /// </summary>
        public override void TriggeredJobComplete(IOperableTrigger trigger, IJobDetail jobDetail, SchedulerInstruction triggerInstCode)
        {
            var jobHashKey = this.RedisJobStoreSchema.JobHashKey(jobDetail.Key);

            var jobDataMapHashKey = this.RedisJobStoreSchema.JobDataMapHashKey(jobDetail.Key);

            var triggerHashKey = this.RedisJobStoreSchema.TriggerHashkey(trigger.Key);

            if (this.Db.KeyExists(jobHashKey))
            {
                Logger.InfoFormat("{0} - Job has completed", jobHashKey);

                if (jobDetail.PersistJobDataAfterExecution)
                {
                    var jobDataMap = jobDetail.JobDataMap;

                    Db.KeyDelete(jobDataMapHashKey);
                    if (jobDataMap != null && !jobDataMap.IsEmpty)
                    {
                        Db.HashSet(jobDataMapHashKey, ConvertToHashEntries(jobDataMap));
                    }
                }

                if (jobDetail.ConcurrentExecutionDisallowed)
                {
                    Db.SetRemove(this.RedisJobStoreSchema.BlockedJobsSet(), jobHashKey);

                    Db.KeyDelete(this.RedisJobStoreSchema.JobBlockedKey(jobDetail.Key));

                    var jobTriggersSetKey = this.RedisJobStoreSchema.JobTriggersSetKey(jobDetail.Key);

                    foreach (var nonConcurrentTriggerHashKey in this.Db.SetMembers(jobTriggersSetKey))
                    {
                        var score =
                            this.Db.SortedSetScore(this.RedisJobStoreSchema.TriggerStateSetKey(RedisTriggerState.Blocked),
                                                   nonConcurrentTriggerHashKey);
                        if (score.HasValue)
                        {
                            this.SetTriggerState(RedisTriggerState.Paused, score.Value, nonConcurrentTriggerHashKey);
                        }
                        else
                        {
                            score =
                                this.Db.SortedSetScoreAsync(
                                    this.RedisJobStoreSchema.TriggerStateSetKey(RedisTriggerState.PausedBlocked),
                                    nonConcurrentTriggerHashKey).Result;

                            if (score.HasValue)
                            {
                                this.SetTriggerState(RedisTriggerState.Paused, score.Value, nonConcurrentTriggerHashKey);
                            }
                        }
                    }
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
            }
            else
            {
                this.Db.SetRemove(this.RedisJobStoreSchema.BlockedJobsSet(), jobHashKey);
            }

            if (this.Db.KeyExists(triggerHashKey))
            {
                if (triggerInstCode == SchedulerInstruction.DeleteTrigger)
                {
                    if (trigger.GetNextFireTimeUtc().HasValue == false)
                    {
                        if (string.IsNullOrEmpty(this.Db.HashGet(triggerHashKey, RedisJobStoreSchema.NextFireTime)))
                        {
                            RemoveTrigger(trigger.Key);
                        }
                    }
                    else
                    {
                        this.RemoveTrigger(trigger.Key);
                        this.SchedulerSignaler.SignalSchedulingChange(null);
                    }
                }
                else if (triggerInstCode == SchedulerInstruction.SetTriggerComplete)
                {
                    this.SetTriggerState(RedisTriggerState.Completed, DateTimeOffset.UtcNow.DateTime.ToUnixTimeMilliSeconds(), triggerHashKey);
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
                else if (triggerInstCode == SchedulerInstruction.SetTriggerError)
                {
                    double score = trigger.GetNextFireTimeUtc().HasValue
                                       ? trigger.GetNextFireTimeUtc().Value.DateTime.ToUnixTimeMilliSeconds() : 0;
                    this.SetTriggerState(RedisTriggerState.Error, score, triggerHashKey);
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
                else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersError)
                {
                    var jobTriggersSetKey = this.RedisJobStoreSchema.JobTriggersSetKey(jobDetail.Key);

                    foreach (var errorTriggerHashKey in this.Db.SetMembersAsync(jobTriggersSetKey).Result)
                    {
                        var nextFireTime = this.Db.HashGetAsync(errorTriggerHashKey.ToString(), RedisJobStoreSchema.NextFireTime).Result;
                        var score        = string.IsNullOrEmpty(nextFireTime) ? 0 : double.Parse(nextFireTime);
                        this.SetTriggerState(RedisTriggerState.Error, score, errorTriggerHashKey);
                    }
                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
                else if (triggerInstCode == SchedulerInstruction.SetAllJobTriggersComplete)
                {
                    var jobTriggerSetKey = this.RedisJobStoreSchema.JobTriggersSetKey(jobDetail.Key);

                    foreach (var completedTriggerHashKey in this.Db.SetMembersAsync(jobTriggerSetKey).Result)
                    {
                        this.SetTriggerState(RedisTriggerState.Completed, DateTimeOffset.UtcNow.DateTime.ToUnixTimeMilliSeconds(),
                                             completedTriggerHashKey);
                    }

                    this.SchedulerSignaler.SignalSchedulingChange(null);
                }
            }
        }
        /// <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;
        }
Exemple #16
0
        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());
        }
Exemple #17
0
        /// <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);
        }
        private void DoUpdateOfMisfiredTrigger(IOperableTrigger trig, bool forceState, InternalTriggerState newStateIfNotComplete, bool recovering)
        {
            ICalendar cal = null;
            if (trig.CalendarName != null)
            {
                cal = RetrieveCalendar(trig.CalendarName);
            }

            signaler.NotifyTriggerListenersMisfired(trig);

            trig.UpdateAfterMisfire(cal);

            // TODO: Decide if we need to replace the whole trigger or could just update the status and next-fire-time
            if (!trig.GetNextFireTimeUtc().HasValue)
            {
                StoreTrigger(trig, null, true, InternalTriggerState.Complete, forceState, recovering);
            }
            else
            {
                StoreTrigger(trig, null, true, newStateIfNotComplete, forceState, false);
            }
        }