/*
* Copyright 2002-2006 the original author or authors.
*
* 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.
*/

using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;

using Quartz;
using Quartz.Impl;
using Quartz.Simpl;
using Quartz.Spi;
using Quartz.Util;

using Spring.Context;
using Spring.Core.IO;
using Spring.Data.Common;
using Spring.Objects.Factory;

namespace Spring.Scheduling.Quartz
{
    /// <summary> 
    /// FactoryObject that sets up a Quartz Scheduler and exposes it for object references.
    /// </summary>
    /// <remarks>
    /// <p>
    /// Allows registration of JobDetails, Calendars and Triggers, automatically
    /// starting the scheduler on initialization and shutting it down on destruction.
    /// In scenarios that just require static registration of jobs at startup, there
    /// is no need to access the Scheduler instance itself in application code.
    /// </p>
    /// 
    /// <p>
    /// For dynamic registration of jobs at runtime, use a object reference to
    /// this SchedulerFactoryObject to get direct access to the Quartz Scheduler
    /// (<see cref="IScheduler" />). This allows you to create new jobs
    /// and triggers, and also to control and monitor the entire Scheduler.
    /// </p>
    /// 
    /// <p>
    /// Note that Quartz instantiates a new Job for each execution, in
    /// contrast to Timer which uses a TimerTask instance that is shared
    /// between repeated executions. Just JobDetail descriptors are shared.
    /// </p>
    /// 
    /// <p>
    /// When using persistent jobs, it is strongly recommended to perform all
    /// operations on the Scheduler within Spring-managed transactions.
    /// Else, database locking will not properly work and might even break.
    /// </p>
    /// <p>
    /// The preferred way to achieve transactional execution is to demarcate
    /// declarative transactions at the business facade level, which will
    /// automatically apply to Scheduler operations performed within those scopes.
    /// Alternatively, define a TransactionProxyFactoryObject for the Scheduler itself.
    /// </p>
    /// </remarks>
    /// <author>Juergen Hoeller</author>
    /// <author>Marko Lahma (.NET)</author>
    /// <seealso cref="IScheduler" />
    /// <seealso cref="ISchedulerFactory" />
    /// <seealso cref="StdSchedulerFactory" />
    public class SchedulerFactoryObject : SchedulerAccessor, IFactoryObject, IObjectNameAware, IApplicationContextAware, IInitializingObject, IDisposable
    {
        /// <summary>
        /// Default thread count to be set to thread pool.
        /// </summary>
        public const int DEFAULT_THREAD_COUNT = 10;
        
        /// <summary>
        /// Property name for thread count in thread pool.
        /// </summary>
        public const string PROP_THREAD_COUNT = "quartz.threadPool.threadCount";

        [ThreadStatic]
        private static IDbProvider configTimeDbProvider;

        [ThreadStatic]
        private static ITaskExecutor configTimeTaskExecutor;

        /// <summary>
        /// Return the IDbProvider for the currently configured Quartz Scheduler,
        /// to be used by LocalDataSourceJobStore.
        /// </summary>
        /// <remarks>
        /// This instance will be set before initialization of the corresponding
        /// Scheduler, and reset immediately afterwards. It is thus only available
        /// during configuration.
        /// </remarks>
        /// <seealso cref="DbProvider" />
        /// <seealso cref="LocalDataSourceJobStore" />
        public static IDbProvider ConfigTimeDbProvider
        {
            get { return configTimeDbProvider; }
        }

        /// <summary>
        /// Return the TaskExecutor for the currently configured Quartz Scheduler,
        /// to be used by LocalTaskExecutorThreadPool.
        /// </summary>
        /// <remarks>
        /// This instance will be set before initialization of the corresponding
        /// Scheduler, and reset immediately afterwards. It is thus only available
        /// during configuration.
        /// </remarks>
        public static ITaskExecutor ConfigTimeTaskExecutor
        {
            get { return configTimeTaskExecutor; }
        }

        private IApplicationContext applicationContext;
        private string applicationContextSchedulerContextKey;
        private bool autoStartup = true;
        private IResource configLocation;
        private IJobFactory jobFactory;
        private bool jobFactorySet;
        private IDictionary quartzProperties;
        private IScheduler  scheduler;
        private IDictionary schedulerContextMap;
        private Type schedulerFactoryType;
        private string schedulerName;
        private TimeSpan startupDelay = TimeSpan.Zero;
        private ITaskExecutor taskExecutor;
        private bool exposeSchedulerInRepository;
        private bool waitForJobsToCompleteOnShutdown;
        private IDbProvider dbProvider;

        /// <summary>
        /// Initializes a new instance of the <see cref="SchedulerFactoryObject"/> class.
        /// </summary>
        public SchedulerFactoryObject()
        {
            schedulerFactoryType = typeof (StdSchedulerFactory);
        }


        /// <summary>
        /// Set the Quartz SchedulerFactory implementation to use.
        /// </summary>
        /// <remarks>
        /// Default is StdSchedulerFactory, reading in the standard
        /// quartz.properties from Quartz' dll. To use custom Quartz
        /// properties, specify "configLocation" or "quartzProperties".
        /// </remarks>
        /// <value>The scheduler factory class.</value>
        /// <seealso cref="StdSchedulerFactory"/>
        /// <seealso cref="ConfigLocation"/>
        /// <seealso cref="QuartzProperties"/>
        public virtual Type SchedulerFactoryType
        {
            set
            {
                if (value == null || !typeof (ISchedulerFactory).IsAssignableFrom(value))
                {
                    throw new ArgumentException("schedulerFactoryType must implement [Quartz.ISchedulerFactory]");
                }
                schedulerFactoryType = value;
            }
        }

        /// <summary>
        /// Set the name of the Scheduler to fetch from the SchedulerFactory.
        /// If not specified, the default Scheduler will be used.
        /// </summary>
        /// <value>The name of the scheduler.</value>
        /// <seealso cref="ISchedulerFactory.GetScheduler(string)"/>
        /// <seealso cref="ISchedulerFactory.GetScheduler()"/>
        public virtual string SchedulerName
        {
            set { schedulerName = value; }
        }

        /// <summary> 
        /// Set the location of the Quartz properties config file, for example
        /// as assembly resource "assembly:quartz.properties".
        /// </summary>
        /// <remarks>
        /// Note: Can be omitted when all necessary properties are specified
        /// locally via this object, or when relying on Quartz' default configuration.
        /// </remarks>
        /// <seealso cref="QuartzProperties" />
        public virtual IResource ConfigLocation
        {
            set { configLocation = value; }
        }

        /// <summary> 
        /// Set Quartz properties, like "quartz.threadPool.type".
        /// </summary>
        /// <remarks>
        /// Can be used to override values in a Quartz properties config file,
        /// or to specify all necessary properties locally.
        /// </remarks>
        /// <seealso cref="ConfigLocation" />
        public virtual IDictionary QuartzProperties
        {
            set { quartzProperties = value; }
        }

        /// <summary>
        /// Set the Spring TaskExecutor to use as Quartz backend.
        /// Exposed as thread pool through the Quartz SPI.
        /// </summary>
        /// <remarks>
        /// By default, a Quartz SimpleThreadPool will be used, configured through
        /// the corresponding Quartz properties.
        /// </remarks>
        /// <value>The task executor.</value>
        /// <seealso cref="QuartzProperties"/>
        /// <seealso cref="LocalTaskExecutorThreadPool"/>
        public virtual ITaskExecutor TaskExecutor
        {
            set { taskExecutor = value; }
        }

        /// <summary> 
        /// Register objects in the Scheduler context via a given Map.
        /// These objects will be available to any Job that runs in this Scheduler.
        /// </summary>
        /// <remarks>
        /// Note: When using persistent Jobs whose JobDetail will be kept in the
        /// database, do not put Spring-managed object or an ApplicationContext
        /// reference into the JobDataMap but rather into the SchedulerContext.
        /// </remarks>
        /// <value>
        /// Map with string keys and any objects as
        /// values (for example Spring-managed objects)
        /// </value>
        /// <seealso cref="JobDetailObject.JobDataAsMap" />
        public virtual IDictionary SchedulerContextAsMap
        {
            set { schedulerContextMap = value; }
        }

        /// <summary>
        /// Set the key of an IApplicationContext reference to expose in the
        /// SchedulerContext, for example "applicationContext". Default is none.
        /// Only applicable when running in a Spring ApplicationContext.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Note: When using persistent Jobs whose JobDetail will be kept in the
        /// database, do not put an IApplicationContext reference into the JobDataMap
        /// but rather into the SchedulerContext.
        /// </p>
        /// 	
        /// <p>
        /// In case of a QuartzJobObject, the reference will be applied to the Job
        /// instance as object property. An "applicationContext" attribute will
        /// correspond to a "setApplicationContext" method in that scenario.
        /// </p>
        /// 	
        /// <p>
        /// Note that ObjectFactory callback interfaces like IApplicationContextAware
        /// are not automatically applied to Quartz Job instances, because Quartz
        /// itself is reponsible for the lifecycle of its Jobs.
        /// </p>
        /// </remarks>
        /// <value>The application context scheduler context key.</value>
        /// <seealso cref="JobDetailObject.ApplicationContextJobDataKey"/>
        public virtual string ApplicationContextSchedulerContextKey
        {
            set { applicationContextSchedulerContextKey = value; }
        }

        /// <summary> 
        /// Set the Quartz JobFactory to use for this Scheduler.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Default is Spring's <see cref="AdaptableJobFactory" />, which supports
        /// standard Quartz <see cref="IJob" /> instances. Note that this default only applies
 	 	/// to a <i>local</i> Scheduler, not to a RemoteScheduler (where setting
 	 	/// a custom JobFactory is not supported by Quartz).
        /// </p>
        /// <p>
        /// Specify an instance of Spring's <see cref="SpringObjectJobFactory" /> here
        /// (typically as an inner object definition) to automatically populate a job's 
        /// object properties from the specified job data map and scheduler context.
        /// </p>
        /// </remarks>
        /// <seealso cref="AdaptableJobFactory" />
        /// <seealso cref="SpringObjectJobFactory" />
        public virtual IJobFactory JobFactory
        {
            set 
            { 
                jobFactory = value;
                jobFactorySet = true;
            }
        }

        /// <summary>
        /// Set whether to expose the Spring-managed <see cref="IScheduler" /> instance in the
        /// Quartz <see cref="SchedulerRepository" />. Default is "false", since the Spring-managed
        /// Scheduler is usually exclusively intended for access within the Spring context.
        /// </summary>
        /// <remarks>
        /// Switch this flag to "true" in order to expose the Scheduler globally.
        /// This is not recommended unless you have an existing Spring application that
        /// relies on this behavior.
        /// </remarks>
        public virtual bool ExposeSchedulerInRepository
        {
            set { exposeSchedulerInRepository = value; }
        }

        /// <summary> 
        /// Set whether to automatically start the scheduler after initialization.
        /// Default is "true"; set this to "false" to allow for manual startup.
        /// </summary>
        public virtual bool AutoStartup
        {
            set { autoStartup = value; }
        }

        /// <summary> 
        /// Set the time span to wait after initialization before
        /// starting the scheduler asynchronously. Default is 0, meaning
        /// immediate synchronous startup on initialization of this object.
        /// </summary>
        /// <remarks>
        /// Setting this to 10 or 20 seconds makes sense if no jobs
        /// should be run before the entire application has started up.
        /// </remarks>
        public virtual TimeSpan StartupDelay
        {
            set { startupDelay = value; }
        }

        /// <summary>
        /// Set whether to wait for running jobs to complete on Shutdown.
        /// Default is "false".
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [wait for jobs to complete on Shutdown]; otherwise, <c>false</c>.
        /// </value>
        /// <seealso cref="IScheduler.Shutdown(bool)"/>
        public virtual bool WaitForJobsToCompleteOnShutdown
        {
            set { waitForJobsToCompleteOnShutdown = value; }
        }

        /// <summary>
        /// Set the default DbProvider to be used by the Scheduler. If set,
        /// this will override corresponding settings in Quartz properties.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Note: If this is set, the Quartz settings should not define
        ///  a job store "dataSource" to avoid meaningless double configuration.
        /// </p>
        /// <p>
        /// A Spring-specific subclass of Quartz' JobStoreSupport will be used.
        /// It is therefore strongly recommended to perform all operations on
        /// the Scheduler within Spring-managed transactions.
        /// Else, database locking will not properly work and might even break
        /// (e.g. if trying to obtain a lock on Oracle without a transaction).
        /// </p>
        /// </remarks>
        /// <seealso cref="QuartzProperties" />
        /// <seealso cref="LocalDataSourceJobStore" />
        public IDbProvider DbProvider
        {
            set { dbProvider = value; }
        }

        /// <summary>
        /// Set the name of the object in the object factory that created this object.
        /// </summary>
        /// <value>The name of the object in the factory.</value>
        /// <remarks>
        /// 	<p>
        /// Invoked after population of normal object properties but before an init
        /// callback like <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
        /// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
        /// method or a custom init-method.
        /// </p>
        /// </remarks>
        public string ObjectName
        {
            set
            {
                if (schedulerName == null)
                {
                    schedulerName = value;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether this <see cref="SchedulerFactoryObject"/> is running.
        /// </summary>
        /// <value><c>true</c> if running; otherwise, <c>false</c>.</value>
        public virtual bool Running
        {
            get
            {
                if (scheduler != null)
                {
                    try
                    {
                        return !scheduler.InStandbyMode;
                    }
                    catch (SchedulerException)
                    {
                        return false;
                    }
                }
                return false;
            }
        }

        #region IApplicationContextAware Members

        /// <summary>
        /// Sets the <see cref="Spring.Context.IApplicationContext"/> that this
        /// object runs in.
        /// </summary>
        /// <value></value>
        /// <remarks>
        /// <p>
        /// Normally this call will be used to initialize the object.
        /// </p>
        /// <p>
        /// Invoked after population of normal object properties but before an
        /// init callback such as
        /// <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
        /// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
        /// or a custom init-method. Invoked after the setting of any
        /// <see cref="Spring.Context.IResourceLoaderAware"/>'s
        /// <see cref="Spring.Context.IResourceLoaderAware.ResourceLoader"/>
        /// property.
        /// </p>
        /// </remarks>
        /// <exception cref="Spring.Context.ApplicationContextException">
        /// In the case of application context initialization errors.
        /// </exception>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If thrown by any application context methods.
        /// </exception>
        /// <exception cref="Spring.Objects.Factory.ObjectInitializationException"/>
        public virtual IApplicationContext ApplicationContext
        {
            set { applicationContext = value; }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Shut down the Quartz scheduler on object factory Shutdown,
        /// stopping all scheduled jobs.
        /// </summary>
        public virtual void Dispose()
        {
            logger.Info("Shutting down Quartz Scheduler");
            scheduler.Shutdown(waitForJobsToCompleteOnShutdown);
        }

        #endregion

        /// <summary>
        /// Template method that determines the Scheduler to operate on.
        /// To be implemented by subclasses.
        /// </summary>
        /// <returns></returns>
        protected override IScheduler GetScheduler()
        {
            return scheduler;
        }

        #region IFactoryObject Members

        /// <summary>
        /// Return an instance (possibly shared or independent) of the object
        /// managed by this factory.
        /// </summary>
        /// <returns>
        /// An instance (possibly shared or independent) of the object managed by
        /// this factory.
        /// </returns>
        /// <remarks>
        /// <note type="caution">
        /// If this method is being called in the context of an enclosing IoC container and
        /// returns <see langword="null"/>, the IoC container will consider this factory
        /// object as not being fully initialized and throw a corresponding (and most
        /// probably fatal) exception.
        /// </note>
        /// </remarks>
        public virtual object GetObject()
        {
            return scheduler;
        }

        /// <summary>
        /// Return the <see cref="System.Type"/> of object that this
        /// <see cref="Spring.Objects.Factory.IFactoryObject"/> creates, or
        /// <see langword="null"/> if not known in advance.
        /// </summary>
        /// <value></value>
        public virtual Type ObjectType
        {
            get { return (scheduler != null) ? scheduler.GetType() : typeof (IScheduler); }
        }

        /// <summary>
        /// Is the object managed by this factory a singleton or a prototype?
        /// </summary>
        /// <value></value>
        public virtual bool IsSingleton
        {
            get { return true; }
        }

        #endregion

        //---------------------------------------------------------------------
        // Implementation of IInitializingObject interface
        //---------------------------------------------------------------------

        #region IInitializingObject Members

        /// <summary>
        /// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
        /// after it has injected all of an object's dependencies.
        /// </summary>
        /// <remarks>
        /// 	<p>
        /// This method allows the object instance to perform the kind of
        /// initialization only possible when all of it's dependencies have
        /// been injected (set), and to throw an appropriate exception in the
        /// event of misconfiguration.
        /// </p>
        /// 	<p>
        /// Please do consult the class level documentation for the
        /// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
        /// description of exactly <i>when</i> this method is invoked. In
        /// particular, it is worth noting that the
        /// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
        /// and <see cref="Spring.Context.IApplicationContextAware"/>
        /// callbacks will have been invoked <i>prior</i> to this method being
        /// called.
        /// </p>
        /// </remarks>
        /// <exception cref="System.Exception">
        /// In the event of misconfiguration (such as the failure to set a
        /// required property) or if initialization fails.
        /// </exception>
        public virtual void AfterPropertiesSet()
        {
            // Create SchedulerFactory instance.
            ISchedulerFactory schedulerFactory = (ISchedulerFactory) ObjectUtils.InstantiateType(schedulerFactoryType);

            InitSchedulerFactory(schedulerFactory);
            
		    if (taskExecutor != null) 
            {
			    // Make given TaskExecutor available for SchedulerFactory configuration.
			    configTimeTaskExecutor = taskExecutor;
		    }
		    if (dbProvider != null) 
            {
			    // Make given db provider available for SchedulerFactory configuration.
			    configTimeDbProvider = dbProvider;
		    }


            try
            {
                // Get Scheduler instance from SchedulerFactory.
                scheduler = CreateScheduler(schedulerFactory, schedulerName);
                PopulateSchedulerContext();

                if (!jobFactorySet && !(scheduler is RemoteScheduler)) 
                {
 	 	            // Use AdaptableJobFactory as default for a local Scheduler, unless when
 	 	            // explicitly given a null value through the "jobFactory" object property.
 	 	            jobFactory = new AdaptableJobFactory();
 	 	        }

                if (jobFactory != null)
                {
                    if (jobFactory is ISchedulerContextAware)
                    {
                        ((ISchedulerContextAware) jobFactory).SchedulerContext = scheduler.Context;
                    }
                    scheduler.JobFactory = jobFactory;
                }
            }
            finally
            {
			    if (taskExecutor != null) 
                {
				    configTimeTaskExecutor = null;
			    }
			    if (dbProvider != null) 
                {
				    configTimeDbProvider = null;
			    }
            }

            RegisterListeners();
            RegisterJobsAndTriggers();

            // Start Scheduler immediately, if demanded.
            if (autoStartup)
            {
                StartScheduler(scheduler, startupDelay);
            }
        }

        #endregion

        /// <summary> 
        /// Load and/or apply Quartz properties to the given SchedulerFactory.
        /// </summary>
        /// <param name="schedulerFactory">the SchedulerFactory to Initialize</param>
        private void InitSchedulerFactory(ISchedulerFactory schedulerFactory)
        {
            if (!(schedulerFactory is StdSchedulerFactory))
            {
                if (configLocation != null || quartzProperties != null || schedulerName != null ||
                    taskExecutor != null || dbProvider != null)
                {

                    throw new ArgumentException("StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);
                }
                // Otherwise assume that no initialization is necessary...
                return;
            }
            NameValueCollection mergedProps = new NameValueCollection();

            // Set necessary default properties here, as Quartz will not apply
            // its default configuration when explicitly given properties.
            if (taskExecutor != null)
            {
                mergedProps[StdSchedulerFactory.PropertyThreadPoolType] =
                    typeof (LocalTaskExecutorThreadPool).AssemblyQualifiedName;
            }
            else
            {
                mergedProps.Set(StdSchedulerFactory.PropertyThreadPoolType, typeof(SimpleThreadPool).AssemblyQualifiedName);
                mergedProps[PROP_THREAD_COUNT] = Convert.ToString(DEFAULT_THREAD_COUNT);
            }

            if (configLocation != null)
            {
                if (logger.IsInfoEnabled)
                {
                    logger.Info("Loading Quartz config from [" + configLocation + "]");
                }
                using (StreamReader sr = new StreamReader(configLocation.InputStream))
                {
                    string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        string[] lineItems = line.Split(new char[] { '=' }, 2);
                        if (lineItems.Length == 2)
                        {
                            mergedProps[lineItems[0].Trim()] = lineItems[1].Trim();
                        }
                    } 
                }
                
            }

            if (quartzProperties != null)
            {
                // if given quartz properties, merge to them to configuration
            	MergePropertiesIntoMap(quartzProperties, mergedProps);
            }

    		if (dbProvider != null) 
            {
                mergedProps.Add(StdSchedulerFactory.PropertyJobStoreType, typeof(LocalDataSourceJobStore).AssemblyQualifiedName);
	        }


            // Make sure to set the scheduler name as configured in the Spring configuration.
            if (schedulerName != null)
            {
                mergedProps.Add(StdSchedulerFactory.PropertySchedulerInstanceName, schedulerName);
            }

            ((StdSchedulerFactory) schedulerFactory).Initialize(mergedProps);
        }

        /// <summary>
        /// Merges the properties into map. This effectively also
        /// overwrites existing properties with same key in map.
        /// </summary>
        /// <param name="properties">The properties to merge into given map.</param>
        /// <param name="map">The map to merge to.</param>
        protected virtual void MergePropertiesIntoMap(IDictionary properties, NameValueCollection map)
        {
            foreach (string key in properties.Keys)
            {
                map[key] = (string) properties[key];
            }
        }


        /// <summary>
        /// Create the Scheduler instance for the given factory and scheduler name.
        /// Called by afterPropertiesSet.
        /// </summary>
        /// <remarks>
        /// Default implementation invokes SchedulerFactory's <code>GetScheduler</code>
        /// method. Can be overridden for custom Scheduler creation.
        /// </remarks>
        /// <param name="schedulerFactory">the factory to create the Scheduler with</param>
        /// <param name="schedName">the name of the scheduler to create</param>
        /// <returns>the Scheduler instance</returns>
        /// <seealso cref="AfterPropertiesSet"/>
        /// <seealso cref="ISchedulerFactory.GetScheduler()"/>
        protected virtual IScheduler CreateScheduler(ISchedulerFactory schedulerFactory, string schedName)
        {
		    SchedulerRepository repository = SchedulerRepository.Instance;
		    lock (repository) 
            {
			    IScheduler existingScheduler = (schedulerName != null ? repository.Lookup(schedulerName) : null);
			    IScheduler newScheduler = schedulerFactory.GetScheduler();
			    if (newScheduler == existingScheduler) {
				    throw new InvalidOperationException(
                        string.Format(
                        "Active Scheduler of name '{0}' already registered in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!", 
                        schedulerName));
			    }
			    if (!exposeSchedulerInRepository) {
				    // Need to explicitly remove it if not intended for exposure,
				    // since Quartz shares the Scheduler instance by default!
				    SchedulerRepository.Instance.Remove(newScheduler.SchedulerName);
			    }
			    return newScheduler;
		    }
        }

        /// <summary>
        /// Expose the specified context attributes and/or the current
        /// IApplicationContext in the Quartz SchedulerContext.
        /// </summary>
        private void PopulateSchedulerContext()
        {
            // Put specified objects into Scheduler context.
            if (schedulerContextMap != null)
            {
                scheduler.Context.PutAll(schedulerContextMap);
            }

            // Register IApplicationContext in Scheduler context.
            if (applicationContextSchedulerContextKey != null)
            {
                if (applicationContext == null)
                {
                    throw new SystemException("SchedulerFactoryObject needs to be set up in an IApplicationContext " +
                                              "to be able to handle an 'applicationContextSchedulerContextKey'");
                }
                scheduler.Context.Put(applicationContextSchedulerContextKey, applicationContext);
            }
        }


        /// <summary>
        /// Start the Quartz Scheduler, respecting the "startDelay" setting.
        /// </summary>
        /// <param name="sched">the Scheduler to start</param>
        /// <param name="startDelay">the time span to wait before starting
        /// the Scheduler asynchronously</param>
        protected virtual void StartScheduler(IScheduler sched, TimeSpan startDelay)
        {
            if (startDelay.TotalSeconds <= 0)
            {
                logger.Info("Starting Quartz Scheduler now");
                sched.Start();
            }
            else
            {
                if (logger.IsInfoEnabled)
                {
                    logger.Info(
                        string.Format("Will start Quartz Scheduler [{0}] in {1} seconds", sched.SchedulerName,
                                      startDelay));
                }
                sched.StartDelayed(startDelay);
            }
        }


        //---------------------------------------------------------------------
        // Implementation of Lifecycle interface
        //---------------------------------------------------------------------

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public virtual void Start()
        {
            if (scheduler != null)
            {
                try
                {
                    scheduler.Start();
                }
                catch (SchedulerException ex)
                {
                    throw new SchedulingException("Could not start Quartz Scheduler", ex);
                }
            }
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public virtual void Stop()
        {
            if (scheduler != null)
            {
                try
                {
                    scheduler.Standby();
                }
                catch (SchedulerException ex)
                {
                    throw new SchedulingException("Could not stop Quartz Scheduler", ex);
                }
            }
        }

    }
}
