/// <summary> /// Create a JobRunShell instance with the given settings. /// </summary> /// <param name="jobRunShellFactory">A handle to the <see cref="IJobRunShellFactory" /> that produced /// this <see cref="JobRunShell" />.</param> /// <param name="scheduler">The <see cref="IScheduler" /> instance that should be made /// available within the <see cref="JobExecutionContext" />.</param> /// <param name="schdCtxt">the <see cref="SchedulingContext" /> that should be used by the /// <see cref="JobRunShell" /> when making updates to the <see cref="IJobStore" />.</param> public JobRunShell(IJobRunShellFactory jobRunShellFactory, IScheduler scheduler, SchedulingContext schdCtxt) { this.jobRunShellFactory = jobRunShellFactory; this.scheduler = scheduler; this.schdCtxt = schdCtxt; log = LogManager.GetLogger(GetType()); }
/// <summary> /// Get all of the Triggers that are associated to the given Job. /// <p> /// If there are no matches, a zero-length array should be returned. /// </p> /// </summary> public virtual Trigger[] GetTriggersForJob(SchedulingContext ctxt, string jobName, string groupName) { ArrayList trigList = new ArrayList(); string jobKey = JobWrapper.GetJobNameKey(jobName, groupName); lock (triggerLock) { for (int i = 0; i < triggers.Count; i++) { TriggerWrapper tw = (TriggerWrapper) triggers[i]; if (tw.jobKey.Equals(jobKey)) { trigList.Add(tw.trigger.Clone()); } } } return (Trigger[]) trigList.ToArray(typeof (Trigger)); }
/// <summary> /// Pause all of the <see cref="Trigger" />s in the given group. /// </summary> public virtual void PauseTriggerGroup(SchedulingContext ctxt, string groupName) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } resources.JobStore.PauseTriggerGroup(ctxt, groupName); NotifySchedulerThread(null); NotifySchedulerListenersPausedTrigger(null, groupName); }
/// <summary> /// Remove (delete) the <see cref="Trigger" /> with the /// given name, and store the new given one - which must be associated /// with the same job. /// </summary> /// <param name="ctxt">The scheduling context.</param> /// <param name="triggerName">The name of the <see cref="Trigger" /> to be removed.</param> /// <param name="groupName">The group name of the <see cref="Trigger" /> to be removed.</param> /// <param name="newTrigger">The new <see cref="Trigger" /> to be stored.</param> /// <returns> /// <see langword="null" /> if a <see cref="Trigger" /> with the given /// name and group was not found and removed from the store, otherwise /// the first fire time of the newly scheduled trigger. /// </returns> public virtual NullableDateTime RescheduleJob(SchedulingContext ctxt, string triggerName, string groupName, Trigger newTrigger) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } newTrigger.Validate(); ICalendar cal = null; if (newTrigger.CalendarName != null) { cal = resources.JobStore.RetrieveCalendar(ctxt, newTrigger.CalendarName); } NullableDateTime ft = newTrigger.ComputeFirstFireTimeUtc(cal); if (!ft.HasValue) { throw new SchedulerException("Based on configured schedule, the given trigger will never fire.", SchedulerException.ErrorClientError); } if (resources.JobStore.ReplaceTrigger(ctxt, triggerName, groupName, newTrigger)) { NotifySchedulerThread(newTrigger.GetNextFireTimeUtc()); NotifySchedulerListenersUnscheduled(triggerName, groupName); NotifySchedulerListenersScheduled(newTrigger); } else { return null; } return ft; }
/// <summary> /// Delete the identified <see cref="IJob" /> from the Scheduler - and any /// associated <see cref="Trigger" />s. /// </summary> /// <returns> true if the Job was found and deleted.</returns> public virtual bool DeleteJob(SchedulingContext ctxt, string jobName, string groupName) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } return resources.JobStore.RemoveJob(ctxt, jobName, groupName); }
/// <summary> /// Schedule the given <see cref="Trigger" /> with the /// <see cref="IJob" /> identified by the <see cref="Trigger" />'s settings. /// </summary> public virtual DateTime ScheduleJob(SchedulingContext ctxt, Trigger trigger) { ValidateState(); if (trigger == null) { throw new SchedulerException("Trigger cannot be null", SchedulerException.ErrorClientError); } trigger.Validate(); ICalendar cal = null; if (trigger.CalendarName != null) { cal = resources.JobStore.RetrieveCalendar(ctxt, trigger.CalendarName); if (cal == null) { throw new SchedulerException(string.Format(CultureInfo.InvariantCulture, "Calendar not found: {0}", trigger.CalendarName), SchedulerException.ErrorPersistenceCalendarDoesNotExist); } } NullableDateTime ft = trigger.ComputeFirstFireTimeUtc(cal); if (!ft.HasValue) { throw new SchedulerException("Based on configured schedule, the given trigger will never fire.", SchedulerException.ErrorClientError); } resources.JobStore.StoreTrigger(ctxt, trigger, false); NotifySchedulerThread(trigger.GetNextFireTimeUtc()); NotifySchedulerListenersScheduled(trigger); return ft.Value; }
/// <summary> /// Create a <see cref="SchedulerScheduler" /> with the given configuration /// properties. /// </summary> /// <seealso cref="SchedulerSchedulerResources" /> public SchedulerScheduler(SchedulerSchedulerResources resources, SchedulingContext ctxt, TimeSpan idleWaitTime, TimeSpan dbRetryInterval) { Log = LogManager.GetLogger(GetType()); this.resources = resources; try { Bind(); } catch (Exception re) { throw new SchedulerException("Unable to bind scheduler to remoting context.", re); } schedThread = new SchedulerSchedulerThread(this, resources, ctxt); if (idleWaitTime > TimeSpan.Zero) { schedThread.IdleWaitTime = idleWaitTime; } if (dbRetryInterval > TimeSpan.Zero) { schedThread.DbFailureRetryInterval = dbRetryInterval; } jobMgr = new ExecutingJobsManager(); AddGlobalJobListener(jobMgr); errLogger = new ErrorLogger(); AddSchedulerListener(errLogger); signaler = new SchedulerSignalerImpl(this, this.schedThread); Log.Info(string.Format(CultureInfo.InvariantCulture, "Scheduler Scheduler v.{0} created.", Version)); }
/// <summary> /// Interrupt all instances of the identified InterruptableJob. /// </summary> public virtual bool Interrupt(SchedulingContext ctxt, string jobName, string groupName) { if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } IList jobs = CurrentlyExecutingJobs; JobDetail jobDetail; bool interrupted = false; foreach (JobExecutionContext jec in jobs) { jobDetail = jec.JobDetail; if (jobName.Equals(jobDetail.Name) && groupName.Equals(jobDetail.Group)) { IJob job = jec.JobInstance; if (job is IInterruptableJob) { ((IInterruptableJob)job).Interrupt(); interrupted = true; } else { throw new UnableToInterruptJobException(string.Format(CultureInfo.InvariantCulture, "Job '{0}' of group '{1}' can not be interrupted, since it does not implement {2}", jobName, groupName, typeof(IInterruptableJob).FullName)); } } } return interrupted; }
/// <summary> /// Get all <see cref="Trigger" /> s that are associated with the /// identified <see cref="JobDetail" />. /// </summary> public virtual Trigger[] GetTriggersOfJob(SchedulingContext ctxt, string jobName, string groupName) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } return resources.JobStore.GetTriggersForJob(ctxt, jobName, groupName); }
/// <summary> /// Resume (un-pause) all triggers - equivalent of calling <see cref="ResumeTriggerGroup(SchedulingContext, string)" /> /// on every group. /// <p> /// If any <see cref="Trigger" /> missed one or more fire-times, then the /// <see cref="Trigger" />'s misfire instruction will be applied. /// </p> /// </summary> /// <seealso cref="PauseAll(SchedulingContext)" /> public virtual void ResumeAll(SchedulingContext ctxt) { ValidateState(); resources.JobStore.ResumeAll(ctxt); NotifySchedulerThread(null); NotifySchedulerListenersResumedTrigger(null, null); }
/// <summary> /// Resume (un-pause) all of the <see cref="JobDetail" />s /// in the given group. /// <p> /// If any of the <see cref="IJob" /> s had <see cref="Trigger" /> s that /// missed one or more fire-times, then the <see cref="Trigger" />'s /// misfire instruction will be applied. /// </p> /// </summary> public virtual void ResumeJobGroup(SchedulingContext ctxt, string groupName) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } resources.JobStore.ResumeJobGroup(ctxt, groupName); NotifySchedulerThread(null); NotifySchedulerListenersResumedJob(null, groupName); }
/// <summary> /// Gets the paused trigger groups. /// </summary> /// <param name="ctxt">The the job scheduling context.</param> /// <returns></returns> public virtual ISet GetPausedTriggerGroups(SchedulingContext ctxt) { return resources.JobStore.GetPausedTriggerGroups(ctxt); }
/// <summary> /// Pause the <see cref="JobDetail" /> with the given /// name - by pausing all of its current <see cref="Trigger" />s. /// </summary> public virtual void PauseJob(SchedulingContext ctxt, string jobName, string groupName) { lock (triggerLock) { Trigger[] t = GetTriggersForJob(ctxt, jobName, groupName); for (int j = 0; j < t.Length; j++) { PauseTrigger(ctxt, t[j].Name, t[j].Group); } } }
/// <summary> /// Pause all of the <see cref="Trigger" />s in the given group. /// <p> /// The JobStore should "remember" that the group is paused, and impose the /// pause on any new triggers that are added to the group while the group is /// paused. /// </p> /// </summary> public virtual void PauseTriggerGroup(SchedulingContext ctxt, string groupName) { lock (triggerLock) { if (pausedTriggerGroups.Contains(groupName)) { return; } pausedTriggerGroups.Add(groupName); string[] names = GetTriggerNames(ctxt, groupName); for (int i = 0; i < names.Length; i++) { PauseTrigger(ctxt, names[i], groupName); } } }
/// <summary> /// Pause the <see cref="Trigger" /> with the given name. /// </summary> public virtual void PauseTrigger(SchedulingContext ctxt, string triggerName, string groupName) { TriggerWrapper tw = (TriggerWrapper) triggersByFQN[TriggerWrapper.GetTriggerNameKey(triggerName, groupName)]; // does the trigger exist? if (tw == null || tw.trigger == null) { return; } // if the trigger is "complete" pausing it does not make sense... if (tw.state == InternalTriggerState.Complete) { return; } lock (triggerLock) { if (tw.state == InternalTriggerState.Blocked) { tw.state = InternalTriggerState.PausedAndBlocked; } else { tw.state = InternalTriggerState.Paused; } timeTriggers.Remove(tw); } }
/// <summary> /// Get the names of all registered <see cref="ICalendar" />s. /// </summary> public virtual string[] GetCalendarNames(SchedulingContext ctxt) { ValidateState(); return resources.JobStore.GetCalendarNames(ctxt); }
/// <summary> /// Notifies the job store job complete. /// </summary> /// <param name="ctxt">The job scheduling context.</param> /// <param name="trigger">The trigger.</param> /// <param name="detail">The detail.</param> /// <param name="instCode">The instruction code.</param> protected internal virtual void NotifyJobStoreJobComplete(SchedulingContext ctxt, Trigger trigger, JobDetail detail, SchedulerInstruction instCode) { resources.JobStore.TriggeredJobComplete(ctxt, trigger, detail, instCode); }
/// <summary> /// Get the names of all known <see cref="Trigger" /> /// groups. /// </summary> public virtual string[] GetTriggerGroupNames(SchedulingContext ctxt) { ValidateState(); return resources.JobStore.GetTriggerGroupNames(ctxt); }
public bool IsTriggerGroupPaused(SchedulingContext ctxt, string groupName) { return resources.JobStore.IsTriggerGroupPaused(ctxt, groupName); }
/// <summary> /// Get the names of all the <see cref="Trigger" />s in /// the given group. /// </summary> public virtual string[] GetTriggerNames(SchedulingContext ctxt, string groupName) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } return resources.JobStore.GetTriggerNames(ctxt, groupName); }
/// <summary> /// Add the <see cref="IJob" /> identified by the given /// <see cref="JobDetail" /> to the Scheduler, and /// associate the given <see cref="Trigger" /> with it. /// <p> /// If the given Trigger does not reference any <see cref="IJob" />, then it /// will be set to reference the Job passed with it into this method. /// </p> /// </summary> public virtual DateTime ScheduleJob(SchedulingContext ctxt, JobDetail jobDetail, Trigger trigger) { ValidateState(); if (jobDetail == null) { throw new SchedulerException("JobDetail cannot be null", SchedulerException.ErrorClientError); } if (trigger == null) { throw new SchedulerException("Trigger cannot be null", SchedulerException.ErrorClientError); } jobDetail.Validate(); if (trigger.JobName == null) { trigger.JobName = jobDetail.Name; trigger.JobGroup = jobDetail.Group; } else if (trigger.JobName != null && !trigger.JobName.Equals(jobDetail.Name)) { throw new SchedulerException("Trigger does not reference given job!", SchedulerException.ErrorClientError); } else if (trigger.JobGroup != null && !trigger.JobGroup.Equals(jobDetail.Group)) { throw new SchedulerException("Trigger does not reference given job!", SchedulerException.ErrorClientError); } trigger.Validate(); ICalendar cal = null; if (trigger.CalendarName != null) { cal = resources.JobStore.RetrieveCalendar(ctxt, trigger.CalendarName); if (cal == null) { throw new SchedulerException(string.Format(CultureInfo.InvariantCulture, "Calendar not found: {0}", trigger.CalendarName), SchedulerException.ErrorPersistenceCalendarDoesNotExist); } } NullableDateTime ft = trigger.ComputeFirstFireTimeUtc(cal); if (!ft.HasValue) { throw new SchedulerException("Based on configured schedule, the given trigger will never fire.", SchedulerException.ErrorClientError); } resources.JobStore.StoreJobAndTrigger(ctxt, jobDetail, trigger); NotifySchedulerThread(trigger.GetNextFireTimeUtc()); NotifySchedulerListenersScheduled(trigger); return ft.Value; }
/// <summary> /// Get the <see cref="JobDetail" /> for the <see cref="IJob" /> /// instance with the given name and group. /// </summary> public virtual JobDetail GetJobDetail(SchedulingContext ctxt, string jobName, string jobGroup) { ValidateState(); if (jobGroup == null) { jobGroup = SchedulerConstants.DefaultGroup; } return resources.JobStore.RetrieveJob(ctxt, jobName, jobGroup); }
/// <summary> /// Add the given <see cref="IJob" /> to the Scheduler - with no associated /// <see cref="Trigger" />. The <see cref="IJob" /> will be 'dormant' until /// it is scheduled with a <see cref="Trigger" />, or <see cref="IScheduler.TriggerJob(string ,string)" /> /// is called for it. /// <p> /// The <see cref="IJob" /> must by definition be 'durable', if it is not, /// SchedulerException will be thrown. /// </p> /// </summary> public virtual void AddJob(SchedulingContext ctxt, JobDetail jobDetail, bool replace) { ValidateState(); if (!jobDetail.Durable && !replace) { throw new SchedulerException("Jobs added with no trigger must be durable.", SchedulerException.ErrorClientError); } resources.JobStore.StoreJob(ctxt, jobDetail, replace); }
/// <summary> /// Get the current state of the identified <see cref="Trigger" />. /// </summary> /// <seealso cref="TriggerState.Normal" /> /// <seealso cref="TriggerState.Paused" /> /// <seealso cref="TriggerState.Complete" /> /// <seealso cref="TriggerState.Error" /> public virtual TriggerState GetTriggerState(SchedulingContext ctxt, string triggerName, string triggerGroup) { ValidateState(); if (triggerGroup == null) { triggerGroup = SchedulerConstants.DefaultGroup; } return resources.JobStore.GetTriggerState(ctxt, triggerName, triggerGroup); }
/// <summary> /// Remove the indicated <see cref="Trigger" /> from the /// scheduler. /// </summary> public virtual bool UnscheduleJob(SchedulingContext ctxt, string triggerName, string groupName) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } if (resources.JobStore.RemoveTrigger(ctxt, triggerName, groupName)) { NotifySchedulerThread(null); NotifySchedulerListenersUnscheduled(triggerName, groupName); } else { return false; } return true; }
/// <summary> /// Add (register) the given <see cref="ICalendar" /> to the Scheduler. /// </summary> public virtual void AddCalendar(SchedulingContext ctxt, string calName, ICalendar calendar, bool replace, bool updateTriggers) { ValidateState(); resources.JobStore.StoreCalendar(ctxt, calName, calendar, replace, updateTriggers); }
/// <summary> /// Trigger the identified <see cref="IJob" /> (Execute it /// now) - with a volatile trigger. /// </summary> public virtual void TriggerJobWithVolatileTrigger(SchedulingContext ctxt, string jobName, string groupName, JobDataMap data) { ValidateState(); if (groupName == null) { groupName = SchedulerConstants.DefaultGroup; } Trigger trig = new SimpleTrigger(NewTriggerId(), SchedulerConstants.DefaultManualTriggers, jobName, groupName, DateTime.UtcNow, null, 0, TimeSpan.Zero); trig.Volatile = true; trig.ComputeFirstFireTimeUtc(null); if (data != null) { trig.JobDataMap = data; } bool collision = true; while (collision) { try { resources.JobStore.StoreTrigger(ctxt, trig, false); collision = false; } catch (ObjectAlreadyExistsException) { trig.Name = NewTriggerId(); } } NotifySchedulerThread(trig.GetNextFireTimeUtc()); NotifySchedulerListenersScheduled(trig); }
/// <summary> /// Delete the identified <see cref="ICalendar" /> from the Scheduler. /// </summary> /// <returns> true if the Calendar was found and deleted.</returns> public virtual bool DeleteCalendar(SchedulingContext ctxt, string calName) { ValidateState(); return resources.JobStore.RemoveCalendar(ctxt, calName); }
/// <summary> /// Get the <see cref="ICalendar" /> instance with the given name. /// </summary> public virtual ICalendar GetCalendar(SchedulingContext ctxt, string calName) { ValidateState(); return resources.JobStore.RetrieveCalendar(ctxt, calName); }
/// <summary> /// Get the names of all of the <see cref="Trigger" /> groups. /// </summary> public virtual string[] GetTriggerGroupNames(SchedulingContext ctxt) { string[] outList; lock (triggerLock) { outList = new string[triggersByGroup.Count]; int outListPos = 0; IEnumerator keys = new HashSet(triggersByGroup.Keys).GetEnumerator(); while (keys.MoveNext()) { outList[outListPos++] = ((string) keys.Current); } } return outList; }