QuartzScheduler.cs :  » Business-Application » Quartz-Enterprise-Scheduler » Quartz » Core » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Business Application » Quartz Enterprise Scheduler 
Quartz Enterprise Scheduler » Quartz » Core » QuartzScheduler.cs
/* 
* Copyright 2004-2009 James House 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License. You may obtain a copy 
* of the License at 
* 
*   http://www.apache.org/licenses/LICENSE-2.0 
*   
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
* License for the specific language governing permissions and limitations 
* under the License.
* 
*/

/*
* Previously Copyright (c) 2001-2004 James House
*/

using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.Remoting;
using System.Threading;

using Common.Logging;
#if NET_20
using NullableDateTimeSystem.NullableSystem.DateTime;
#else
using Nullables;
#endif

using Quartz;
using Quartz.Collection;
using Quartz.Impl;
using Quartz.Listener;
using Quartz.Simpl;
using Quartz.Spi;

namespace Quartz.Core{
    /// <summary>
    /// This is the heart of Quartz, an indirect implementation of the <see cref="IScheduler" />
    /// interface, containing methods to schedule <see cref="IJob" />s,
    /// register <see cref="IJobListener" /> instances, etc.
    /// </summary>
    /// <seealso cref="IScheduler" />
    /// <seealso cref="QuartzSchedulerThread" />
    /// <seealso cref="IJobStore" />
    /// <seealso cref="IThreadPool" />
    /// <author>James House</author>
    /// <author>Marko Lahma (.NET)</author>
    public class QuartzScheduler : MarshalByRefObject, IRemotableQuartzScheduler
    {
        private readonly ILog Log;
        private static readonly FileVersionInfo versionInfo;


        private readonly QuartzSchedulerResources resources;

        private readonly QuartzSchedulerThread schedThread;
        private readonly SchedulerContext context = new SchedulerContext();

        private readonly IDictionary jobListeners = new Hashtable(10);
        private readonly IDictionary globalJobListeners = new Hashtable(10);
        private readonly IDictionary triggerListeners = new Hashtable(10);
        private readonly IDictionary globalTriggerListeners = new Hashtable(10);
        private readonly ArrayList schedulerListeners = new ArrayList(10);
        private IJobFactory jobFactory = new SimpleJobFactory();
        internal ExecutingJobsManager jobMgr = null;
        internal ErrorLogger errLogger = null;
        private readonly ISchedulerSignaler signaler;
        private readonly Random random = new Random();
        private readonly ArrayList holdToPreventGC = new ArrayList(5);
        private bool signalOnSchedulingChange = true;
        private bool closed = false;
        private bool shuttingDown = false;
        private NullableDateTime initialStart = null;

        /// <summary>
        /// Initializes the <see cref="QuartzScheduler"/> class.
        /// </summary>
        static QuartzScheduler()
        {
            Assembly asm = Assembly.GetAssembly(typeof(QuartzScheduler));
            versionInfo = FileVersionInfo.GetVersionInfo(asm.Location);
        }

        /// <summary>
        /// Gets the version of the Quartz Scheduler.
        /// </summary>
        /// <value>The version.</value>
        public string Version
        {
            get { return versionInfo.FileVersion; }
        }

        /// <summary>
        /// Gets the version major.
        /// </summary>
        /// <value>The version major.</value>
        public static string VersionMajor
        {
            get { return versionInfo.FileMajorPart.ToString(CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Gets the version minor.
        /// </summary>
        /// <value>The version minor.</value>
        public static string VersionMinor
        {
            get { return versionInfo.FileMinorPart.ToString(CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Gets the version iteration.
        /// </summary>
        /// <value>The version iteration.</value>
        public static string VersionIteration
        {
            get { return versionInfo.FileBuildPart.ToString(CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Gets the scheduler signaler.
        /// </summary>
        /// <value>The scheduler signaler.</value>
        public virtual ISchedulerSignaler SchedulerSignaler
        {
            get { return signaler; }
        }

        /// <summary>
        /// Returns the name of the <see cref="QuartzScheduler" />.
        /// </summary>
        public virtual string SchedulerName
        {
            get { return resources.Name; }
        }

        /// <summary> 
        /// Returns the instance Id of the <see cref="QuartzScheduler" />.
        /// </summary>
        public virtual string SchedulerInstanceId
        {
            get { return resources.InstanceId; }
        }


        /// <summary>
        /// Returns the <see cref="SchedulerContext" /> of the <see cref="IScheduler" />.
        /// </summary>
        public virtual SchedulerContext SchedulerContext
        {
            get { return context; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to signal on scheduling change.
        /// </summary>
        /// <value>
        ///   <c>true</c> if schduler should signal on scheduling change; otherwise, <c>false</c>.
        /// </value>
        public virtual bool SignalOnSchedulingChange
        {
            get { return signalOnSchedulingChange; }
            set { signalOnSchedulingChange = value; }
        }

        /// <summary>
        /// Reports whether the <see cref="IScheduler" /> is paused.
        /// </summary>
        public virtual bool InStandbyMode
        {
            get { return schedThread.Paused; }
        }

        /// <summary>
        /// Gets the job store class.
        /// </summary>
        /// <value>The job store class.</value>
        public virtual Type JobStoreClass
        {
            get { return resources.JobStore.GetType(); }
        }

        /// <summary>
        /// Gets the thread pool class.
        /// </summary>
        /// <value>The thread pool class.</value>
        public virtual Type ThreadPoolClass
        {
            get { return resources.ThreadPool.GetType(); }
        }

        /// <summary>
        /// Gets the size of the thread pool.
        /// </summary>
        /// <value>The size of the thread pool.</value>
        public virtual int ThreadPoolSize
        {
            get { return resources.ThreadPool.PoolSize; }
        }

        /// <summary>
        /// Reports whether the <see cref="IScheduler" /> has been Shutdown.
        /// </summary>
        public virtual bool IsShutdown
        {
            get { return closed; }
        }

        /// <summary>
        /// Return a list of <see cref="JobExecutionContext" /> objects that
        /// represent all currently executing Jobs in this Scheduler instance.
        /// <p>
        /// This method is not cluster aware.  That is, it will only return Jobs
        /// currently executing in this Scheduler instance, not across the entire
        /// cluster.
        /// </p>
        /// <p>
        /// Note that the list returned is an 'instantaneous' snap-shot, and that as
        /// soon as it's returned, the true list of executing jobs may be different.
        /// </p>
        /// </summary>
        public virtual IList CurrentlyExecutingJobs
        {
            get { return jobMgr.ExecutingJobs; }
        }

        /// <summary>
        /// Get a List containing all of the <see cref="IJobListener" />
        /// s in the <see cref="IScheduler" />'s<i>global</i> list.
        /// </summary>
        public virtual IList GlobalJobListeners
        {
            get { return new ArrayList(globalJobListeners.Values); }
        }

        /// <summary>
        /// Get a Set containing the names of all the <i>non-global</i><see cref="IJobListener" />
        /// s registered with the <see cref="IScheduler" />.
        /// </summary>
        public virtual ISet JobListenerNames
        {
            get
            {
                lock (jobListeners)
                {
                    return new HashSet(jobListeners.Keys);
                }
            }
        }


        /// <summary>
        /// Get the <i>global</i><see cref="IJobListener" />
        /// that has the given name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public IJobListener GetGlobalJobListener(string name)
        {
            lock (globalJobListeners)
            {
                return (IJobListener)globalJobListeners[name];
            }
        }

        /// <summary>
        /// Get a list containing all of the <see cref="ITriggerListener" />
        /// s in the <see cref="IScheduler" />'s<i>global</i> list.
        /// </summary>
        public virtual IList GlobalTriggerListeners
        {
            get 
            { 
                lock (globalTriggerListeners)
                {
                    return new ArrayList(globalTriggerListeners.Values);
                } 
            }
        }

        /// <summary>
        /// Get a Set containing the names of all the <i>non-global</i><see cref="ITriggerListener" />
        /// s registered with the <see cref="IScheduler" />.
        /// </summary>
        public virtual ISet TriggerListenerNames
        {
            get
            {
                lock (triggerListeners)
                {
                    return new HashSet(triggerListeners.Keys);
                }
            }
        }

        /// <summary>
        /// Get a List containing all of the <see cref="ISchedulerListener" />
        /// s registered with the <see cref="IScheduler" />.
        /// </summary>
        public virtual IList SchedulerListeners
        {
            get
            {
                lock (schedulerListeners)
                {
                    return (IList)schedulerListeners.Clone();
                }
            }
        }

        /// <summary>
        /// Gets or sets the job factory.
        /// </summary>
        /// <value>The job factory.</value>
        public virtual IJobFactory JobFactory
        {
            get { return jobFactory; }
            set
            {
                if (value == null)
                {
                    throw new ArgumentException("JobFactory cannot be set to null!");
                }

                Log.Info("JobFactory set to: " + value);

                jobFactory = value;
            }
        }



        /// <summary>
        /// Create a <see cref="QuartzScheduler" /> with the given configuration
        /// properties.
        /// </summary>
        /// <seealso cref="QuartzSchedulerResources" />
        public QuartzScheduler(QuartzSchedulerResources 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 QuartzSchedulerThread(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, "Quartz Scheduler v.{0} created.", Version));
        }

        /// <summary>
        /// Bind the scheduler to remoting infrastructure.
        /// </summary>
        private void Bind()
        {
            if (resources.SchedulerExporter != null)
            {
                resources.SchedulerExporter.Bind(this);
            }
        }

        /// <summary>
        /// Un-bind the scheduler from remoting infrastructure.
        /// </summary>
        private void UnBind()
        {
            if (resources.SchedulerExporter != null)
            {
                resources.SchedulerExporter.UnBind(this);
            }
        }

        /// <summary>
        /// Adds an object that should be kept as reference to prevent
        /// it from being garbage collected.
        /// </summary>
        /// <param name="obj">The obj.</param>
        public virtual void AddNoGCObject(object obj)
        {
            holdToPreventGC.Add(obj);
        }

        /// <summary>
        /// Removes the object from garbae collection protected list.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public virtual bool RemoveNoGCObject(object obj)
        {
            return CollectionUtil.Remove(holdToPreventGC, obj);
        }

        /// <summary>
        /// Starts the <see cref="QuartzScheduler" />'s threads that fire <see cref="Trigger" />s.
        /// <p>
        /// All <see cref="Trigger" />s that have misfired will
        /// be passed to the appropriate TriggerListener(s).
        /// </p>
        /// </summary>
        public virtual void Start()
        {
            if (shuttingDown || closed)
            {
                throw new SchedulerException("The Scheduler cannot be restarted after Shutdown() has been called.");
            }

            if (!initialStart.HasValue)
            {
                initialStart = DateTime.UtcNow;
                resources.JobStore.SchedulerStarted();
                StartPlugins();
            }

            schedThread.TogglePause(false);

            Log.Info(string.Format(CultureInfo.InvariantCulture, "Scheduler {0} started.", resources.GetUniqueIdentifier()));
        }

        public void StartDelayed(TimeSpan delay)
        {
            if (shuttingDown || closed) 
            {
                throw new SchedulerException(
                        "The Scheduler cannot be restarted after Shutdown() has been called.");
            }

            DelayedSchedulerStarter starter = new DelayedSchedulerStarter(this, delay, Log);
            Thread t = new Thread(new ThreadStart(starter.Run));
            t.Start();
        }

        /// <summary>
        /// Helper class to start scheduler in a delayed fashion.
        /// </summary>
        private class DelayedSchedulerStarter
        {
            private readonly QuartzScheduler scheduler;
            private readonly TimeSpan delay;
            private readonly ILog logger;

            public DelayedSchedulerStarter(QuartzScheduler scheduler, TimeSpan delay, ILog logger)
            {
                this.scheduler = scheduler;
                this.delay = delay;
                this.logger = logger;
            }

            public void Run()
            {
                try
                {
                    Thread.Sleep(delay);
                }
                catch (ThreadInterruptedException) { }
                try
                {
                    scheduler.Start();
                }
                catch (SchedulerException se)
                {
                    logger.Error("Unable to start secheduler after startup delay.", se);
                }
            }
        }

        /// <summary>
        /// Temporarily halts the <see cref="QuartzScheduler" />'s firing of <see cref="Trigger" />s.
        /// <p>
        /// The scheduler is not destroyed, and can be re-started at any time.
        /// </p>
        /// </summary>
        public virtual void Standby()
        {
            schedThread.TogglePause(true);
            Log.Info(string.Format(CultureInfo.InvariantCulture, "Scheduler {0} paused.", resources.GetUniqueIdentifier()));
        }

        /// <summary>
        /// Gets the running since.
        /// </summary>
        /// <value>The running since.</value>
        public virtual NullableDateTime RunningSince
        {
            get { return initialStart; }
        }

        /// <summary>
        /// Gets the number of jobs executed.
        /// </summary>
        /// <value>The number of jobs executed.</value>
        public virtual int NumJobsExecuted
        {
            get { return jobMgr.NumJobsFired; }
        }

        /// <summary>
        /// Gets a value indicating whether this scheduler supports persistence.
        /// </summary>
        /// <value><c>true</c> if supports persistence; otherwise, <c>false</c>.</value>
        public virtual bool SupportsPersistence
        {
            get { return resources.JobStore.SupportsPersistence; }
        }

        /// <summary>
        /// Halts the <see cref="QuartzScheduler" />'s firing of <see cref="Trigger" />s,
        /// and cleans up all resources associated with the QuartzScheduler.
        /// Equivalent to <see cref="Shutdown(bool)" />.
        /// <p>
        /// The scheduler cannot be re-started.
        /// </p>
        /// </summary>
        public virtual void Shutdown()
        {
            Shutdown(false);
        }

        /// <summary>
        /// Halts the <see cref="QuartzScheduler" />'s firing of <see cref="Trigger" />s,
        /// and cleans up all resources associated with the QuartzScheduler.
        /// <p>
        /// The scheduler cannot be re-started.
        /// </p>
        /// </summary>
        /// <param name="waitForJobsToComplete">
        /// if <see langword="true" /> the scheduler will not allow this method
        /// to return until all currently executing jobs have completed.
        /// </param>
        public virtual void Shutdown(bool waitForJobsToComplete)
        {
            if (shuttingDown || closed)
            {
                return;
            }

            shuttingDown = true;

            Log.Info(string.Format(CultureInfo.InvariantCulture, "Scheduler {0} shutting down.", resources.GetUniqueIdentifier()));
            Standby();

            schedThread.Halt();

            resources.ThreadPool.Shutdown(waitForJobsToComplete);

            if (waitForJobsToComplete)
            {
                while (jobMgr.NumJobsCurrentlyExecuting > 0)
                {
                    try
                    {
                        Thread.Sleep(100);
                    }
                    catch (ThreadInterruptedException)
                    {
                    }
                }
            }

            // Scheduler thread may have be waiting for the fire time of an acquired 
            // trigger and need time to release the trigger once halted, so make sure
            // the thread is dead before continuing to shutdown the job store.
            try
            {
                schedThread.Join();
            }
            catch (Exception)
            {
                ;
            }

            closed = true;

            resources.JobStore.Shutdown();

            NotifySchedulerListenersShutdown();

            ShutdownPlugins();

            SchedulerRepository.Instance.Remove(resources.Name);

            holdToPreventGC.Clear();

            try
            {
                UnBind();
            }
            catch (RemotingException)
            {
            }

            Log.Info(string.Format(CultureInfo.InvariantCulture, "Scheduler {0} Shutdown complete.", resources.GetUniqueIdentifier()));
        }

        /// <summary>
        /// Validates the state.
        /// </summary>
        public virtual void ValidateState()
        {
            if (IsShutdown)
            {
                throw new SchedulerException("The Scheduler has been Shutdown.");
            }

            // other conditions to check (?)
        }

        /// <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>
        /// 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>
        /// 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>
        /// 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>
        /// 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>
        /// 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;
        }


        private string NewTriggerId()
        {
            long r = NextLong(random);
            if (r < 0)
            {
                r = -r;
            }
            return "MT_" + Convert.ToString(r, CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Creates a new positive random number 
        /// </summary>
        /// <param name="random">The last random obtained</param>
        /// <returns>Returns a new positive random number</returns>
        public static long NextLong(Random random)
        {
            long temporaryLong = random.Next();
            temporaryLong = (temporaryLong << 32) + random.Next();
            if (random.Next(-1, 1) < 0)
            {
                return -temporaryLong;
            }
            else
            {
                return temporaryLong;
            }
        }

        /// <summary>
        /// Trigger the identified <see cref="IJob" /> (Execute it now) - with a non-volatile trigger.
        /// </summary>
        public virtual void TriggerJob(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 = false;
            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>
        /// 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>
        /// Pause the <see cref="Trigger" /> with the given name.
        /// </summary>
        public virtual void PauseTrigger(SchedulingContext ctxt, string triggerName, string groupName)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            resources.JobStore.PauseTrigger(ctxt, triggerName, groupName);
            NotifySchedulerThread(null);
            NotifySchedulerListenersPausedTrigger(triggerName, groupName);
        }

        /// <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> 
        /// 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)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            resources.JobStore.PauseJob(ctxt, jobName, groupName);
            NotifySchedulerThread(null);
            NotifySchedulerListenersPausedJob(jobName, groupName);
        }

        /// <summary>
        /// Pause all of the <see cref="JobDetail" />s in the
        /// given group - by pausing all of their <see cref="Trigger" />s.
        /// </summary>
        public virtual void PauseJobGroup(SchedulingContext ctxt, string groupName)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            resources.JobStore.PauseJobGroup(ctxt, groupName);
            NotifySchedulerThread(null);
            NotifySchedulerListenersPausedJob(null, groupName);
        }

        /// <summary>
        /// Resume (un-pause) the <see cref="Trigger" /> with the given
        /// name.
        /// <p>
        /// If the <see cref="Trigger" /> missed one or more fire-times, then the
        /// <see cref="Trigger" />'s misfire instruction will be applied.
        /// </p>
        /// </summary>
        public virtual void ResumeTrigger(SchedulingContext ctxt, string triggerName, string groupName)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            resources.JobStore.ResumeTrigger(ctxt, triggerName, groupName);
            NotifySchedulerThread(null);
            NotifySchedulerListenersResumedTrigger(triggerName, groupName);
        }

        /// <summary>
        /// Resume (un-pause) all of the <see cref="Trigger" />s in the
        /// given 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>
        public virtual void ResumeTriggerGroup(SchedulingContext ctxt, string groupName)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            resources.JobStore.ResumeTriggerGroup(ctxt, groupName);
            NotifySchedulerThread(null);
            NotifySchedulerListenersResumedTrigger(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>
        /// Resume (un-pause) the <see cref="JobDetail" /> with
        /// the given name.
        /// <p>
        /// If any of the <see cref="IJob" />'s<see cref="Trigger" /> s missed one
        /// or more fire-times, then the <see cref="Trigger" />'s misfire
        /// instruction will be applied.
        /// </p>
        /// </summary>
        public virtual void ResumeJob(SchedulingContext ctxt, string jobName, string groupName)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            resources.JobStore.ResumeJob(ctxt, jobName, groupName);
            NotifySchedulerThread(null);
            NotifySchedulerListenersResumedJob(jobName, groupName);
        }

        /// <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>
        /// Pause all triggers - equivalent of calling <see cref="PauseTriggerGroup(SchedulingContext, string)" />
        /// on every group.
        /// <p>
        /// When <see cref="ResumeAll" /> is called (to un-pause), trigger misfire
        /// instructions WILL be applied.
        /// </p>
        /// </summary>
        /// <seealso cref="ResumeAll(SchedulingContext)" />
        /// <seealso cref="PauseJob" />
        public virtual void PauseAll(SchedulingContext ctxt)
        {
            ValidateState();

            resources.JobStore.PauseAll(ctxt);
            NotifySchedulerThread(null);
            NotifySchedulerListenersPausedTrigger(null, null);
        }

        /// <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>
        /// Get the names of all known <see cref="IJob" /> groups.
        /// </summary>
        public virtual string[] GetJobGroupNames(SchedulingContext ctxt)
        {
            ValidateState();

            return resources.JobStore.GetJobGroupNames(ctxt);
        }

        /// <summary>
        /// Get the names of all the <see cref="IJob" />s in the
        /// given group.
        /// </summary>
        public virtual string[] GetJobNames(SchedulingContext ctxt, string groupName)
        {
            ValidateState();

            if (groupName == null)
            {
                groupName = SchedulerConstants.DefaultGroup;
            }

            return resources.JobStore.GetJobNames(ctxt, groupName);
        }

        /// <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>
        /// Get the names of all known <see cref="Trigger" />
        /// groups.
        /// </summary>
        public virtual string[] GetTriggerGroupNames(SchedulingContext ctxt)
        {
            ValidateState();
            return resources.JobStore.GetTriggerGroupNames(ctxt);
        }

        /// <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> 
        /// 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>
        /// Get the <see cref="Trigger" /> instance with the given name and
        /// group.
        /// </summary>
        public virtual Trigger GetTrigger(SchedulingContext ctxt, string triggerName, string triggerGroup)
        {
            ValidateState();

            if (triggerGroup == null)
            {
                triggerGroup = SchedulerConstants.DefaultGroup;
            }

            return resources.JobStore.RetrieveTrigger(ctxt, triggerName, triggerGroup);
        }

        /// <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>
        /// 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>
        /// 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 registered <see cref="ICalendar" />s.
        /// </summary>
        public virtual string[] GetCalendarNames(SchedulingContext ctxt)
        {
            ValidateState();
            return resources.JobStore.GetCalendarNames(ctxt);
        }

        /// <summary>
        /// Add the given <see cref="IJobListener" /> to the
        /// <see cref="IScheduler" />'s<i>global</i> list.
        /// <p>
        /// Listeners in the 'global' list receive notification of execution events
        /// for ALL <see cref="IJob" />s.
        /// </p>
        /// </summary>
        public void AddGlobalJobListener(IJobListener jobListener)
        {
            if (jobListener.Name == null || jobListener.Name.Length == 0)
            {
                throw new ArgumentException("JobListener name cannot be empty.");
            }
            lock (globalJobListeners)
            {
                globalJobListeners.Add(jobListener.Name, jobListener);
            }
        }

        /// <summary>
        /// Add the given <see cref="IJobListener" /> to the
        /// <see cref="IScheduler" />'s list, of registered <see cref="IJobListener" />s.
        /// </summary>
        public virtual void AddJobListener(IJobListener jobListener)
        {
            if (jobListener.Name == null || jobListener.Name.Length == 0)
            {
                throw new ArgumentException("JobListener name cannot be empty.");
            }
            lock (jobListener)
            {
                jobListeners.Add(jobListener.Name, jobListener);
            }
        }

        /// <summary> 
        /// Remove the given <see cref="IJobListener" /> from the
        /// <see cref="IScheduler" />'s list of <i>global</i> listeners.
        /// </summary>
        /// <returns> 
        /// true if the identifed listener was found in the list, and removed.
        /// </returns>
        public virtual bool RemoveGlobalJobListener(IJobListener jobListener)
        {
            return RemoveGlobalJobListener((jobListener == null) ? null : jobListener.Name);
        }


        /// <summary>
        /// Remove the identifed <see cref="IJobListener" /> from the <see cref="IScheduler" />'s
        /// list of <i>global</i> listeners. 
        /// </summary>
        /// <param name="name"></param>
        /// <returns>true if the identifed listener was found in the list, and removed.</returns>
        public bool RemoveGlobalJobListener(string name)
        {
            lock (globalJobListeners)
            {
                if (globalJobListeners.Contains(name))
                {
                    globalJobListeners.Remove(name);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Remove the identifed <see cref="IJobListener" /> from
        /// the <see cref="IScheduler" />'s list of registered listeners.
        /// </summary>
        /// <returns> 
        /// true if the identifed listener was found in the list, and removed.
        /// </returns>
        public virtual bool RemoveJobListener(string name)
        {
            lock (jobListeners)
            {
                if (jobListeners.Contains(name))
                {
                    jobListeners.Remove(name);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Get the <i>non-global</i><see cref="IJobListener" /> that has the given name.
        /// </summary>
        public virtual IJobListener GetJobListener(string name)
        {
            lock (jobListeners)
            {
                return (IJobListener) jobListeners[name];
            }
        }

        /// <summary>
        /// Add the given <see cref="ITriggerListener" /> to the
        /// <see cref="IScheduler" />'s<i>global</i> list.
        /// <p>
        /// Listeners in the 'global' list receive notification of execution events
        /// for ALL <see cref="Trigger" />s.
        /// </p>
        /// </summary>
        public virtual void AddGlobalTriggerListener(ITriggerListener triggerListener)
        {
            if (triggerListener.Name == null || triggerListener.Name.Trim().Length == 0)
            {
                throw new ArgumentException("TriggerListener name cannot be empty.");
            }

            lock (globalTriggerListeners)
            {
                globalTriggerListeners[triggerListener.Name] = triggerListener;
            }
        }

        /// <summary> 
        /// Add the given <see cref="ITriggerListener" /> to the
        /// <see cref="IScheduler" />'s list, of registered <see cref="ITriggerListener" />s.
        /// </summary>
        public virtual void AddTriggerListener(ITriggerListener triggerListener)
        {
            if (triggerListener.Name == null || triggerListener.Name.Trim().Length == 0)
            {
                throw new ArgumentException("TriggerListener name cannot be empty.");
            }

            lock (triggerListeners)
            {
                triggerListeners[triggerListener.Name] = triggerListener;
            }
        }

        /// <summary> 
        /// Remove the given <see cref="ITriggerListener" /> from
        /// the <see cref="IScheduler" />'s list of <i>global</i> listeners.
        /// </summary>
        /// <returns> 
        /// true if the identifed listener was found in the list, and removed.
        /// </returns>
        public virtual bool RemoveGlobalTriggerListener(ITriggerListener triggerListener)
        {
            return RemoveGlobalTriggerListener((triggerListener == null) ? null : triggerListener.Name);
        }

        /// <summary>
        ///  Remove the identifed <see cref="ITriggerListener" /> from the <see cref="IScheduler" />'s
        /// list of <i>global</i> listeners.
        /// </summary>
        /// <param name="name"></param>
        /// <returns> true if the identifed listener was found in the list, and removed</returns>
        public bool RemoveGlobalTriggerListener(string name)
        {
            lock (globalTriggerListeners)
            {
                if (globalTriggerListeners.Contains(name))
                {
                    globalTriggerListeners.Remove(name);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Remove the identifed <see cref="ITriggerListener" />
        /// from the <see cref="IScheduler" />'s list of registered listeners.
        /// </summary>
        /// <returns>
        /// true if the identifed listener was found in the list, and removed.
        /// </returns>
        public virtual bool RemoveTriggerListener(string name)
        {
            lock (triggerListeners)
            {
                if (triggerListeners.Contains(name))
                {
                    triggerListeners.Remove(name);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Get the <i>non-global</i> <see cref="ITriggerListener" />
        /// that has the given name.
        /// </summary>
        public ITriggerListener GetTriggerListener(string name)
        {
            lock (triggerListeners)
            {
                return (ITriggerListener) triggerListeners[name];
            }
        }


        /// <summary>
        /// Get the <i>global</i> <see cref="ITriggerListener" /> that
        /// has the given name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ITriggerListener GetGlobalTriggerListener(string name)
        {
            lock (globalTriggerListeners)
            {
                return (ITriggerListener)globalTriggerListeners[name];
            }
        }
    

        /// <summary>
        /// Register the given <see cref="ISchedulerListener" /> with the
        /// <see cref="IScheduler" />.
        /// </summary>
        public void AddSchedulerListener(ISchedulerListener schedulerListener)
        {
            lock (schedulerListeners)
            {
                schedulerListeners.Add(schedulerListener);
            }
        }

        /// <summary>
        /// Remove the given <see cref="ISchedulerListener" /> from the
        /// <see cref="IScheduler" />.
        /// </summary>
        /// <returns> 
        /// true if the identifed listener was found in the list, and removed.
        /// </returns>
        public virtual bool RemoveSchedulerListener(ISchedulerListener schedulerListener)
        {
            lock (schedulerListeners)
            {
                return CollectionUtil.Remove(schedulerListeners, schedulerListener);
            }
        }


        protected internal void NotifyJobStoreJobVetoed(SchedulingContext ctxt,
                Trigger trigger, JobDetail detail, SchedulerInstruction instCode)
        {

            resources.JobStore.TriggeredJobComplete(ctxt, trigger, detail, instCode);
        }

        /// <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>
        /// Notifies the scheduler thread.
        /// </summary>
        protected internal virtual void NotifySchedulerThread(NullableDateTime candidateNewNextFireTimeUtc)
        {
            if (SignalOnSchedulingChange)
            {
                schedThread.SignalSchedulingChange(candidateNewNextFireTimeUtc);
            }
        }

        private IList BuildTriggerListenerList(string[] additionalListeners)
        {
            IList listeners = GlobalTriggerListeners;
            for (int i = 0; i < additionalListeners.Length; i++)
            {
                ITriggerListener tl = GetTriggerListener(additionalListeners[i]);

                if (tl != null)
                {
                    listeners.Add(tl);
                }
                else
                {
                    throw new SchedulerException(string.Format(CultureInfo.InvariantCulture, "TriggerListener '{0}' not found.", additionalListeners[i]),
                                                 SchedulerException.ErrorTriggerListenerNotFound);
                }
            }

            return listeners;
        }

        private IList BuildJobListenerList(string[] additionalListeners)
        {
            IList listeners = GlobalJobListeners;
            for (int i = 0; i < additionalListeners.Length; i++)
            {
                IJobListener jl = GetJobListener(additionalListeners[i]);

                if (jl != null)
                {
                    listeners.Add(jl);
                }
                else
                {
                    throw new SchedulerException(string.Format(CultureInfo.InvariantCulture, "JobListener '{0}' not found.", additionalListeners[i]),
                                                 SchedulerException.ErrorJobListenerNotFound);
                }
            }

            return listeners;
        }

        /// <summary>
        /// Notifies the trigger listeners about fired trigger.
        /// </summary>
        /// <param name="jec">The job execution context.</param>
        /// <returns></returns>
        public virtual bool NotifyTriggerListenersFired(JobExecutionContext jec)
        {
            // build a list of all trigger listeners that are to be notified...
            IList listeners = BuildTriggerListenerList(jec.Trigger.TriggerListenerNames);

            bool vetoedExecution = false;

            // notify all trigger listeners in the list
            foreach (ITriggerListener tl in listeners)
            {
                try
                {
                    tl.TriggerFired(jec.Trigger, jec);

                    if (tl.VetoJobExecution(jec.Trigger, jec))
                    {
                        vetoedExecution = true;
                    }
                }
                catch (Exception e)
                {
                    SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "TriggerListener '{0}' threw exception: {1}", tl.Name, e.Message), e);
                    se.ErrorCode = SchedulerException.ErrorTriggerListener;
                    throw se;
                }
            }

            return vetoedExecution;
        }


        /// <summary>
        /// Notifies the trigger listeners about misfired trigger.
        /// </summary>
        /// <param name="trigger">The trigger.</param>
        public virtual void NotifyTriggerListenersMisfired(Trigger trigger)
        {
            // build a list of all trigger listeners that are to be notified...
            IList listeners = BuildTriggerListenerList(trigger.TriggerListenerNames);

            // notify all trigger listeners in the list
            foreach (ITriggerListener tl in listeners)
            {
                try
                {
                    tl.TriggerMisfired(trigger);
                }
                catch (Exception e)
                {
                    SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "TriggerListener '{0}' threw exception: {1}", tl.Name, e.Message), e);
                    se.ErrorCode = SchedulerException.ErrorTriggerListener;
                    throw se;
                }
            }
        }

        /// <summary>
        /// Notifies the trigger listeners of completion.
        /// </summary>
        /// <param name="jec">The job executution context.</param>
        /// <param name="instCode">The instruction code to report to triggers.</param>
        public virtual void NotifyTriggerListenersComplete(JobExecutionContext jec, SchedulerInstruction instCode)
        {
            // build a list of all trigger listeners that are to be notified...
            IList listeners = BuildTriggerListenerList(jec.Trigger.TriggerListenerNames);

            // notify all trigger listeners in the list
            foreach (ITriggerListener tl in listeners)
            {
                try
                {
                    tl.TriggerComplete(jec.Trigger, jec, instCode);
                }
                catch (Exception e)
                {
                    SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "TriggerListener '{0}' threw exception: {1}", tl.Name, e.Message), e);
                    se.ErrorCode = SchedulerException.ErrorTriggerListener;
                    throw se;
                }
            }
        }

        /// <summary>
        /// Notifies the job listeners about job to be executed.
        /// </summary>
        /// <param name="jec">The jec.</param>
        public virtual void NotifyJobListenersToBeExecuted(JobExecutionContext jec)
        {
            // build a list of all job listeners that are to be notified...
            IList listeners = BuildJobListenerList(jec.JobDetail.JobListenerNames);

            // notify all job listeners
            foreach (IJobListener jl in listeners)
            {
                try
                {
                    jl.JobToBeExecuted(jec);
                }
                catch (Exception e)
                {
                    SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "JobListener '{0}' threw exception: {1}", jl.Name, e.Message), e);
                    se.ErrorCode = SchedulerException.ErrorJobListener;
                    throw se;
                }
            }
        }

        /// <summary>
        /// Notifies the job listeners that job exucution was vetoed.
        /// </summary>
        /// <param name="jec">The job execution context.</param>
        public virtual void NotifyJobListenersWasVetoed(JobExecutionContext jec)
        {
            // build a list of all job listeners that are to be notified...
            IList listeners = BuildJobListenerList(jec.JobDetail.JobListenerNames);

            // notify all job listeners
            foreach (IJobListener jl in listeners)
            {
                try
                {
                    jl.JobExecutionVetoed(jec);
                }
                catch (Exception e)
                {
                    SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "JobListener '{0}' threw exception: {1}", jl.Name, e.Message), e);
                    se.ErrorCode = SchedulerException.ErrorJobListener;
                    throw se;
                }
            }
        }

        /// <summary>
        /// Notifies the job listeners that job was executed.
        /// </summary>
        /// <param name="jec">The jec.</param>
        /// <param name="je">The je.</param>
        public virtual void NotifyJobListenersWasExecuted(JobExecutionContext jec, JobExecutionException je)
        {
            // build a list of all job listeners that are to be notified...
            IList listeners = BuildJobListenerList(jec.JobDetail.JobListenerNames);

            // notify all job listeners
            foreach (IJobListener jl in listeners)
            {
                try
                {
                    jl.JobWasExecuted(jec, je);
                }
                catch (Exception e)
                {
                    SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "JobListener '{0}' threw exception: {1}", jl.Name, e.Message), e);
                    se.ErrorCode = SchedulerException.ErrorJobListener;
                    throw se;
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about scheduler error.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <param name="se">The se.</param>
        public virtual void NotifySchedulerListenersError(string msg, SchedulerException se)
        {
            // build a list of all scheduler listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.SchedulerError(msg, se);
                }
                catch (Exception e)
                {
                    Log.Error("Error while notifying SchedulerListener of error: ", e);
                    Log.Error("  Original error (for notification) was: " + msg, se);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about job that was scheduled.
        /// </summary>
        /// <param name="trigger">The trigger.</param>
        public virtual void NotifySchedulerListenersScheduled(Trigger trigger)
        {
            // build a list of all scheduler listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.JobScheduled(trigger);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of scheduled job.  Triger={0}", trigger.FullName), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about job that was unscheduled.
        /// </summary>
        /// <param name="triggerName">Name of the trigger.</param>
        /// <param name="triggerGroup">The trigger group.</param>
        public virtual void NotifySchedulerListenersUnscheduled(string triggerName, string triggerGroup)
        {
            // build a list of all scheduler listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.JobUnscheduled(triggerName, triggerGroup);
                }
                catch (Exception e)
                {
                    Log.Error(
                        string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of unscheduled job.  Triger={0}.{1}", triggerGroup, triggerName), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about finalized trigger.
        /// </summary>
        /// <param name="trigger">The trigger.</param>
        public virtual void NotifySchedulerListenersFinalized(Trigger trigger)
        {
            // build a list of all scheduler listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.TriggerFinalized(trigger);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of finalized trigger.  Triger={0}", trigger.FullName), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about paused trigger.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="group">The group.</param>
        public virtual void NotifySchedulerListenersPausedTrigger(string name, string group)
        {
            // build a list of all job listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.TriggersPaused(name, group);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of paused trigger/group.  Triger={0}.{1}", group, name), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners resumed trigger.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="group">The group.</param>
        public virtual void NotifySchedulerListenersResumedTrigger(string name, string group)
        {
            // build a list of all job listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.TriggersResumed(name, group);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of resumed trigger/group.  Triger={0}.{1}", group, name), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about paused job.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="group">The group.</param>
        public virtual void NotifySchedulerListenersPausedJob(string name, string group)
        {
            // build a list of all job listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.JobsPaused(name, group);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of paused job/group.  Job={0}.{1}", group, name), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about resumed job.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="group">The group.</param>
        public virtual void NotifySchedulerListenersResumedJob(string name, string group)
        {
            // build a list of all job listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.JobsResumed(name, group);
                }
                catch (Exception e)
                {
                    Log.Error(string.Format(CultureInfo.InvariantCulture, "Error while notifying SchedulerListener of resumed job/group.  Job={0}.{1}", group, name), e);
                }
            }
        }

        /// <summary>
        /// Notifies the scheduler listeners about scheduler shutdown.
        /// </summary>
        public virtual void NotifySchedulerListenersShutdown()
        {
            // build a list of all job listeners that are to be notified...
            IList schedListeners = SchedulerListeners;

            // notify all scheduler listeners
            foreach (ISchedulerListener sl in schedListeners)
            {
                try
                {
                    sl.SchedulerShutdown();
                }
                catch (Exception e)
                {
                    Log.Error("Error while notifying SchedulerListener of Shutdown.", e);
                }
            }
        }


        /// <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;
        }

        private void ShutdownPlugins()
        {
            foreach (ISchedulerPlugin plugin in resources.SchedulerPlugins)
            {
                plugin.Shutdown();
            }
        }

        private void StartPlugins()
        {
            foreach (ISchedulerPlugin plugin in resources.SchedulerPlugins)
            {
                plugin.Start();
            }
        }

        public bool IsJobGroupPaused(SchedulingContext ctxt, string groupName)
        {
            return resources.JobStore.IsJobGroupPaused(ctxt, groupName);
        }

        public bool IsTriggerGroupPaused(SchedulingContext ctxt, string groupName)
        {
            return resources.JobStore.IsTriggerGroupPaused(ctxt, groupName);
        }

        ///<summary>
        ///Obtains a lifetime service object to control the lifetime policy for this instance.
        ///</summary>
        public override object InitializeLifetimeService()
        {
            // overriden to initialize null life time service,
            // this basically means that remoting object will live as long
            // as the application lives
            return null;
        }
    }

    /// <summary>
    /// ErrorLogger - Scheduler Listener Class
    /// </summary>
    internal class ErrorLogger : SchedulerListenerSupport
    {
        public override void SchedulerError(string msg, SchedulerException cause)
        {
            Log.Error(msg, cause);
        }
    }

    /////////////////////////////////////////////////////////////////////////////
    //
    // ExecutingJobsManager - Job Listener Class
    //
    /////////////////////////////////////////////////////////////////////////////
    internal class ExecutingJobsManager : IJobListener
    {
        public virtual string Name
        {
            get { return GetType().FullName; }
        }

        public virtual int NumJobsCurrentlyExecuting
        {
            get
            {
                lock (executingJobs.SyncRoot)
                {
                    return executingJobs.Count;
                }
            }
        }

        public virtual int NumJobsFired
        {
            get { return numJobsFired; }
        }

        public virtual IList ExecutingJobs
        {
            get
            {
                lock (executingJobs.SyncRoot)
                {
                    return ArrayList.ReadOnly(new ArrayList(new ArrayList(executingJobs.Values)));
                }
            }
        }

        internal IDictionary executingJobs = new Hashtable();

        internal int numJobsFired = 0;

        public virtual void JobToBeExecuted(JobExecutionContext context)
        {
            numJobsFired++;

            lock (executingJobs.SyncRoot)
            {
                executingJobs[context.Trigger.FireInstanceId] = context;
            }
        }

        public virtual void JobWasExecuted(JobExecutionContext context, JobExecutionException jobException)
        {
            lock (executingJobs.SyncRoot)
            {
                executingJobs.Remove(context.Trigger.FireInstanceId);
            }
        }

        public virtual void JobExecutionVetoed(JobExecutionContext context)
        {
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.