(bundle.JobDetail.JobType);
+#else
+ return ObjectUtils.InstantiateType(bundle.JobDetail.JobType);
+#endif
+ }
+
+ ///
+ /// Adapt the given job object to the Quartz Job interface.
+ ///
+ ///
+ /// The default implementation supports straight Quartz Jobs
+ /// as well as Runnables, which get wrapped in a DelegatingJob.
+ ///
+ ///
+ /// The original instance of the specified job class.
+ ///
+ /// The adapted Quartz Job instance.
+ ///
+ protected virtual IJob AdaptJob(object jobObject)
+ {
+ if (jobObject is IJob)
+ {
+ return (IJob)jobObject;
+ }
+ if (jobObject is ThreadStart)
+ {
+ return new DelegatingJob((ThreadStart)jobObject);
+ }
+ if (jobObject is IThreadRunnable)
+ {
+ return new DelegatingJob(((IThreadRunnable)jobObject).Run);
+ }
+
+ string message = string.Format("Unable to execute job class [{0}]: only [IJob] and [ThreadStart] supported.", jobObject.GetType().FullName);
+ throw new ArgumentException(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/DelegatingJob.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/DelegatingJob.cs
new file mode 100644
index 00000000..544abcc0
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/DelegatingJob.cs
@@ -0,0 +1,77 @@
+/*
+* Copyright 2002-2010 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.Threading;
+
+using Quartz;
+
+using Spring.Util;
+
+#if QUARTZ_2_0
+using JobExecutionContext = Quartz.IJobExecutionContext;
+#else
+using JobExecutionContext = Quartz.JobExecutionContext;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Simple Quartz IJob adapter that delegates to a
+ /// given instance.
+ ///
+ ///
+ /// Typically used in combination with property injection on the
+ /// Runnable instance, receiving parameters from the Quartz JobDataMap
+ /// that way instead of via the JobExecutionContext.
+ ///
+ /// Juergen Hoeller
+ /// Marko Lahma (.NET)
+ ///
+ ///
+ public class DelegatingJob : IJob
+ {
+ private readonly ThreadStart delegateInstance;
+
+ ///
+ /// Return the wrapped Runnable implementation.
+ ///
+ /// The delegate.
+ public virtual ThreadStart Delegate
+ {
+ get { return delegateInstance; }
+ }
+
+ ///
+ /// Create a new DelegatingJob.
+ ///
+ ///
+ /// The Runnable implementation to delegate to.
+ ///
+ public DelegatingJob(ThreadStart delegateInstance)
+ {
+ AssertUtils.ArgumentNotNull(delegateInstance, "delegateInstance", "Delegate must not be null");
+ this.delegateInstance = delegateInstance;
+ }
+
+
+ ///
+ /// Delegates execution to the underlying ThreadStart.
+ ///
+ public virtual void Execute(JobExecutionContext context)
+ {
+ delegateInstance.Invoke();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/IJobDetailAwareTrigger.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/IJobDetailAwareTrigger.cs
new file mode 100644
index 00000000..8882120b
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/IJobDetailAwareTrigger.cs
@@ -0,0 +1,52 @@
+/*
+* Copyright 2002-2010 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.
+*/
+
+#if QUARTZ_2_0
+using JobDetail = Quartz.IJobDetail;
+#else
+using JobDetail = Quartz.JobDetail;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Interface to be implemented by Quartz Triggers that are aware
+ /// of the JobDetail object that they are associated with.
+ ///
+ ///
+ ///
+ /// SchedulerFactoryObject will auto-detect Triggers that implement this
+ /// interface and register them for the respective JobDetail accordingly.
+ ///
+ ///
+ ///
+ /// The alternative is to configure a Trigger for a Job name and group:
+ /// This involves the need to register the JobDetail object separately
+ /// with SchedulerFactoryObject.
+ ///
+ ///
+ /// Juergen Hoeller
+ ///
+ ///
+ public interface IJobDetailAwareTrigger
+ {
+ ///
+ /// Return the JobDetail that this Trigger is associated with.
+ ///
+ /// The associated JobDetail, or null if none
+ JobDetail JobDetail { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ISchedulerContextAware.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ISchedulerContextAware.cs
new file mode 100644
index 00000000..35c6422c
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ISchedulerContextAware.cs
@@ -0,0 +1,41 @@
+/*
+* Copyright 2002-2010 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 Quartz;
+using Quartz.Spi;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Callback interface to be implemented by Spring-managed
+ /// Quartz artifacts that need access to the SchedulerContext
+ /// (without having natural access to it).
+ ///
+ ///
+ /// Currently only supported for custom JobFactory implementations
+ /// that are passed in via Spring's SchedulerFactoryObject.
+ ///
+ /// Juergen Hoeller
+ ///
+ ///
+ public interface ISchedulerContextAware
+ {
+ ///
+ /// Set the SchedulerContext of the current Quartz Scheduler.
+ ///
+ ///
+ SchedulerContext SchedulerContext { set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ISchedulingTaskExecutor.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ISchedulingTaskExecutor.cs
new file mode 100644
index 00000000..4cbd1c1d
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ISchedulingTaskExecutor.cs
@@ -0,0 +1,32 @@
+/*
+* Copyright 2002-2010 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.
+*/
+
+namespace Spring.Scheduling
+{
+ ///
+ ///
+ ///
+ public interface ISchedulingTaskExecutor : ITaskExecutor
+ {
+ ///
+ /// Gets a value indicating whether´this instance prefers short lived tasks.
+ ///
+ ///
+ /// true if prefers short lived tasks; otherwise, false .
+ ///
+ bool PrefersShortLivedTasks { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ITaskExecutor.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ITaskExecutor.cs
new file mode 100644
index 00000000..93952687
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/ITaskExecutor.cs
@@ -0,0 +1,31 @@
+/*
+* Copyright 2002-2010 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.Threading;
+
+namespace Spring.Scheduling
+{
+ ///
+ ///
+ ///
+ public interface ITaskExecutor
+ {
+ ///
+ /// Executes this instance.
+ ///
+ void Execute(ThreadStart runnable);
+ }
+}
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/LocalDataSourceJobStore.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/LocalDataSourceJobStore.cs
new file mode 100644
index 00000000..bec9799e
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/LocalDataSourceJobStore.cs
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2002-2010 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 Quartz;
+using Quartz.Impl.AdoJobStore;
+using Quartz.Util;
+
+using Spring.Data.Support;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Subclass of Quartz's JobStoreCMT class that delegates to a Spring-managed
+ /// DataSource instead of using a Quartz-managed connection pool. This JobStore
+ /// will be used if SchedulerFactoryObject's "dbProvider" property is set.
+ ///
+ ///
+ /// Operations performed by this JobStore will properly participate in any
+ /// kind of Spring-managed transaction, as it uses Spring's DataSourceUtils
+ /// connection handling methods that are aware of a current transaction.
+ ///
+ /// Note that all Quartz Scheduler operations that affect the persistent
+ /// job store should usually be performed within active transactions,
+ /// as they assume to get proper locks etc.
+ ///
+ /// Juergen Hoeller
+ /// Marko Lahma (.NET)
+ ///
+ ///
+ public class LocalDataSourceJobStore : JobStoreCMT
+ {
+ ///
+ /// Name used for the transactional ConnectionProvider for Quartz.
+ /// This provider will delegate to the local Spring-managed DataSource.
+ ///
+ ///
+ ///
+ public const string TX_DATA_SOURCE_PREFIX = "springTxDataSource.";
+
+ private Data.Common.IDbProvider dbProvider;
+
+ ///
+ /// Gets or sets the name of the instance.
+ ///
+ /// The name of the instance.
+ public override string InstanceName
+ {
+ get { return base.InstanceName; }
+ set
+ {
+ // use to catch property setting
+ base.InstanceName = value;
+ DataSource = TX_DATA_SOURCE_PREFIX + InstanceName;
+ // Register transactional ConnectionProvider for Quartz.
+ // Absolutely needs thread-bound DataSource to initialize.
+ dbProvider = SchedulerFactoryObject.ConfigTimeDbProvider;
+ if (dbProvider == null)
+ {
+ throw new SchedulerConfigException(
+ "No db provider found for configuration - " +
+ "'DbProvider' property must be set on SchedulerFactoryObject");
+ }
+ DBConnectionManager.Instance.AddConnectionProvider(
+ TX_DATA_SOURCE_PREFIX + InstanceName, new SpringDbProviderAdapter(dbProvider));
+
+ }
+ }
+
+ ///
+ /// Gets the non managed TX connection.
+ ///
+ ///
+ protected override ConnectionAndTransactionHolder GetNonManagedTXConnection()
+ {
+ ConnectionTxPair pair = ConnectionUtils.DoGetConnection(dbProvider);
+ return new ConnectionAndTransactionHolder(pair.Connection, pair.Transaction);
+ }
+
+ ///
+ /// Closes the connection.
+ ///
+ /// The connection and transaction holder.
+ protected override void CloseConnection(ConnectionAndTransactionHolder connectionAndTransactionHolder)
+ {
+ // Will work for transactional and non-transactional connections.
+ ConnectionUtils.DisposeConnection(connectionAndTransactionHolder.Connection, dbProvider);
+ }
+ }
+}
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/LocalTaskExecutorThreadPool.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/LocalTaskExecutorThreadPool.cs
new file mode 100644
index 00000000..8b1062af
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/LocalTaskExecutorThreadPool.cs
@@ -0,0 +1,160 @@
+/*
+* Copyright 2002-2010 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 Common.Logging;
+using Quartz;
+using Quartz.Spi;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Quartz ThreadPool adapter that delegates to a Spring-managed
+ /// TaskExecutor instance, specified on SchedulerFactoryObject.
+ ///
+ /// Juergen Hoeller
+ ///
+ public class LocalTaskExecutorThreadPool : IThreadPool
+ {
+ ///
+ /// Logger available to subclasses.
+ ///
+ private readonly ILog logger;
+
+ private ITaskExecutor taskExecutor;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LocalTaskExecutorThreadPool()
+ {
+ logger = LogManager.GetLogger(GetType());
+ }
+
+ ///
+ /// Logger instance.
+ ///
+ protected ILog Logger
+ {
+ get { return logger; }
+ }
+
+ ///
+ /// Gets the size of the pool.
+ ///
+ /// The size of the pool.
+ public virtual int PoolSize
+ {
+ get { return - 1; }
+ }
+
+ ///
+ /// Inform the of the Scheduler instance's Id,
+ /// prior to initialize being invoked.
+ ///
+ public string InstanceId
+ {
+ set { }
+ }
+
+ ///
+ /// Inform the of the Scheduler instance's name,
+ /// prior to initialize being invoked.
+ ///
+ public string InstanceName
+ {
+ set { }
+ }
+
+ ///
+ /// Called by the QuartzScheduler before the is
+ /// used, in order to give the it a chance to Initialize.
+ ///
+ public virtual void Initialize()
+ {
+ // Absolutely needs thread-bound TaskExecutor to Initialize.
+ taskExecutor = SchedulerFactoryObject.ConfigTimeTaskExecutor;
+ if (taskExecutor == null)
+ {
+ throw new SchedulerConfigException("No local TaskExecutor found for configuration - " +
+ "'taskExecutor' property must be set on SchedulerFactoryObject");
+ }
+ }
+
+ ///
+ /// Called by the QuartzScheduler to inform the
+ /// that it should free up all of it's resources because the scheduler is
+ /// shutting down.
+ ///
+ ///
+ public virtual void Shutdown(bool waitForJobsToComplete)
+ {
+ }
+
+
+ ///
+ /// Execute the given in the next
+ /// available .
+ ///
+ ///
+ ///
+ ///
+ /// The implementation of this interface should not throw exceptions unless
+ /// there is a serious problem (i.e. a serious misconfiguration). If there
+ /// are no available threads, rather it should either queue the Runnable, or
+ /// block until a thread is available, depending on the desired strategy.
+ ///
+ public virtual bool RunInThread(IThreadRunnable runnable)
+ {
+ if (runnable == null)
+ {
+ return false;
+ }
+ try
+ {
+ taskExecutor.Execute(runnable.Run);
+ return true;
+ }
+ catch (TaskRejectedException ex)
+ {
+ logger.Error("Task has been rejected by TaskExecutor", ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Determines the number of threads that are currently available in in
+ /// the pool. Useful for determining the number of times
+ /// can be called before returning
+ /// false.
+ ///
+ ///
+ /// the number of currently available threads
+ ///
+ ///
+ /// The implementation of this method should block until there is at
+ /// least one available thread.
+ ///
+ public virtual int BlockForAvailableThreads()
+ {
+ // The present implementation always returns 1, making Quartz (1.6)
+ // always schedule any tasks that it feels like scheduling.
+ // This could be made smarter for specific TaskExecutors,
+ // for example calling getMaximumPoolSize() - getActiveCount()
+ // on a java.util.concurrent.ThreadPoolExecutor.
+ return 1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/MethodInvokingJob.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/MethodInvokingJob.cs
new file mode 100644
index 00000000..a89343a7
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/MethodInvokingJob.cs
@@ -0,0 +1,96 @@
+/*
+* Copyright 2002-2010 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.Reflection;
+
+using Common.Logging;
+
+using Quartz;
+
+using Spring.Objects.Support;
+
+#if QUARTZ_2_0
+using JobExecutionContext = Quartz.IJobExecutionContext;
+#else
+using JobExecutionContext = Quartz.JobExecutionContext;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Quartz Job implementation that invokes a specified method.
+ /// Automatically applied by MethodInvokingJobDetailFactoryObject.
+ ///
+ public class MethodInvokingJob : QuartzJobObject
+ {
+ private static readonly ILog logger = LogManager.GetLogger(typeof(MethodInvokingJob));
+ private MethodInvoker methodInvoker;
+ private string errorMessage;
+
+ ///
+ /// Set the MethodInvoker to use.
+ ///
+ public virtual MethodInvoker MethodInvoker
+ {
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentException("Method invoker cannot be null", "value");
+ }
+ methodInvoker = value;
+ errorMessage =
+ string.Format("Could not invoke method '{0}' on target object [{1}]", methodInvoker.TargetMethod,
+ methodInvoker.TargetObject);
+ }
+ }
+
+
+ ///
+ /// Invoke the method via the MethodInvoker.
+ ///
+ ///
+ protected override void ExecuteInternal(JobExecutionContext context)
+ {
+ if (methodInvoker == null)
+ {
+ throw new JobExecutionException("Could not execute job when method invoker is null");
+ }
+ try
+ {
+ context.Result = methodInvoker.Invoke();
+ }
+ catch (TargetInvocationException ex)
+ {
+ logger.Error(errorMessage, ex.GetBaseException());
+ if (ex.GetBaseException() is JobExecutionException)
+ {
+ // -> JobExecutionException, to be logged at info level by Quartz
+ throw ex.GetBaseException();
+ }
+ // -> "unhandled exception", to be logged at error level by Quartz
+ throw new JobMethodInvocationFailedException(methodInvoker, ex.GetBaseException());
+ }
+ catch (Exception ex)
+ {
+ // -> "unhandled exception", to be logged at error level by Quartz
+ throw new JobMethodInvocationFailedException(methodInvoker, ex.GetBaseException());
+ }
+ }
+ }
+
+}
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/MethodInvokingRunnable.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/MethodInvokingRunnable.cs
new file mode 100644
index 00000000..e60635cb
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/MethodInvokingRunnable.cs
@@ -0,0 +1,139 @@
+/*
+* Copyright 2002-2010 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.Reflection;
+
+using Common.Logging;
+
+using Quartz;
+
+using Spring.Objects.Factory;
+using Spring.Objects.Support;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Adapter that implements the Runnable interface as a configurable
+ /// method invocation based on Spring's MethodInvoker.
+ ///
+ ///
+ ///
+ /// Derives from ArgumentConvertingMethodInvoker, inheriting common
+ /// configuration properties from MethodInvoker.
+ ///
+ ///
+ ///
+ /// Useful to generically encapsulate a method invocation as timer task for
+ /// java.util.Timer, in combination with a DelegatingTimerTask adapter.
+ /// Can also be used with JDK 1.5's java.util.concurrent.Executor
+ /// abstraction, which works with plain Runnables.
+ ///
+ ///
+ /// Extended by Spring's MethodInvokingTimerTaskFactoryObject adapter
+ /// for TimerTask. Note that you can populate a
+ /// ScheduledTimerTask object with a plain MethodInvokingRunnable instance
+ /// as well, which will automatically get wrapped with a DelegatingTimerTask.
+ ///
+ ///
+ /// Juergen Hoeller
+ ///
+ ///
+ public class MethodInvokingRunnable : ArgumentConvertingMethodInvoker, IInitializingObject, IThreadRunnable
+ {
+ ///
+ /// Logger instance shared by this instance and its sub-class instances.
+ ///
+ private readonly ILog logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MethodInvokingRunnable()
+ {
+ logger = LogManager.GetLogger(GetType());
+ }
+
+ ///
+ /// Logger instance.
+ ///
+ protected ILog Logger
+ {
+ get { return logger; }
+ }
+
+ ///
+ /// Gets the invocation failure message.
+ ///
+ /// The invocation failure message.
+ protected virtual string InvocationFailureMessage
+ {
+ get { return string.Format("Invocation of method '{0}' on target object [{1}] failed", TargetMethod, TargetObject); }
+ }
+
+ ///
+ /// Invoked by an
+ /// after it has injected all of an object's dependencies.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Please do consult the class level documentation for the
+ /// interface for a
+ /// description of exactly when this method is invoked. In
+ /// particular, it is worth noting that the
+ ///
+ /// and
+ /// callbacks will have been invoked prior to this method being
+ /// called.
+ ///
+ ///
+ ///
+ /// In the event of misconfiguration (such as the failure to set a
+ /// required property) or if initialization fails.
+ ///
+ public virtual void AfterPropertiesSet()
+ {
+ Prepare();
+ }
+
+ ///
+ /// This method has to be implemented in order that starting of the thread causes the object's
+ /// run method to be called in that separately executing thread.
+ ///
+ public virtual void Run()
+ {
+ try
+ {
+ Invoke();
+ }
+ catch (TargetInvocationException ex)
+ {
+ logger.Error(InvocationFailureMessage, ex);
+ // Do not throw exception, else the main loop of the Timer will stop!
+ }
+ catch (Exception ex)
+ {
+ logger.Error(InvocationFailureMessage, ex);
+ // Do not throw exception, else the main loop of the Timer will stop!
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/QuartzJobObject.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/QuartzJobObject.cs
new file mode 100644
index 00000000..98391757
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/QuartzJobObject.cs
@@ -0,0 +1,90 @@
+/*
+* Copyright 2002-2010 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 Quartz;
+
+using Spring.Objects;
+
+#if QUARTZ_2_0
+using JobExecutionContext = Quartz.IJobExecutionContext;
+#else
+using JobExecutionContext = Quartz.JobExecutionContext;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Simple implementation of the Quartz Job interface, applying the
+ /// passed-in JobDataMap and also the SchedulerContext as object property
+ /// values. This is appropriate because a new Job instance will be created
+ /// for each execution. JobDataMap entries will override SchedulerContext
+ /// entries with the same keys.
+ ///
+ ///
+ ///
+ /// For example, let's assume that the JobDataMap contains a key
+ /// "myParam" with value "5": The Job implementation can then expose
+ /// a object property "myParam" of type int to receive such a value,
+ /// i.e. a method "setMyParam(int)". This will also work for complex
+ /// types like business objects etc.
+ ///
+ ///
+ ///
+ /// Note: The QuartzJobObject class itself only implements the standard
+ /// Quartz IJob interface. Let your subclass explicitly implement the
+ /// Quartz IStatefulJob interface to mark your concrete job object as stateful.
+ ///
+ ///
+ /// Juergen Hoeller
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public abstract class QuartzJobObject : IJob
+ {
+ ///
+ /// This implementation applies the passed-in job data map as object property
+ /// values, and delegates to ExecuteInternal afterwards.
+ ///
+ ///
+ public void Execute(JobExecutionContext context)
+ {
+ try
+ {
+ ObjectWrapper bw = new ObjectWrapper(this);
+ MutablePropertyValues pvs = new MutablePropertyValues();
+ pvs.AddAll(context.Scheduler.Context);
+ pvs.AddAll(context.MergedJobDataMap);
+ bw.SetPropertyValues(pvs, true);
+ }
+ catch (SchedulerException ex)
+ {
+ throw new JobExecutionException(ex);
+ }
+ ExecuteInternal(context);
+ }
+
+ ///
+ /// Execute the actual job. The job data map will already have been
+ /// applied as object property values by execute. The contract is
+ /// exactly the same as for the standard Quartz execute method.
+ ///
+ ///
+ protected abstract void ExecuteInternal(JobExecutionContext context);
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulerAccessorObject.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulerAccessorObject.cs
new file mode 100644
index 00000000..477e06c4
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulerAccessorObject.cs
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2002-2010 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 Quartz;
+using Quartz.Impl;
+
+using Spring.Objects.Factory;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Spring class for accessing a Quartz Scheduler, i.e. for registering jobs,
+ /// triggers and listeners on a given instance.
+ ///
+ /// Juergen Hoeller
+ /// Marko Lahma (.NET)
+ ///
+ ///
+ public class SchedulerAccessorObject : SchedulerAccessor, IObjectFactoryAware, IInitializingObject
+ {
+ private string schedulerName;
+ private IScheduler scheduler;
+ private IObjectFactory objectFactory;
+
+ ///
+ /// Specify the Quartz Scheduler to operate on via its scheduler name in the Spring
+ /// application context or also in the Quartz {@link org.quartz.impl.SchedulerRepository}.
+ ///
+ ///
+ /// Schedulers can be registered in the repository through custom bootstrapping,
+ /// e.g. via the or
+ /// factory classes.
+ /// However, in general, it's preferable to use Spring's
+ /// which includes the job/trigger/listener capabilities of this accessor as well.
+ ///
+ public string SchedulerName
+ {
+ set { schedulerName = value; }
+ }
+
+ ///
+ /// Return the Quartz Scheduler instance that this accessor operates on.
+ ///
+ protected IScheduler Scheduler
+ {
+ set { scheduler = value; }
+ }
+
+ ///
+ /// Template method that determines the Scheduler to operate on.
+ ///
+ ///
+ protected override IScheduler GetScheduler()
+ {
+ return scheduler;
+ }
+
+ ///
+ /// Return the Quartz Scheduler instance that this accessor operates on.
+ ///
+ public IObjectFactory ObjectFactory
+ {
+ set { objectFactory = value; }
+ }
+
+
+ ///
+ /// Invoked by an
+ /// after it has injected all of an object's dependencies.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Please do consult the class level documentation for the
+ /// interface for a
+ /// description of exactly when this method is invoked. In
+ /// particular, it is worth noting that the
+ ///
+ /// and
+ /// callbacks will have been invoked prior to this method being
+ /// called.
+ ///
+ ///
+ ///
+ /// In the event of misconfiguration (such as the failure to set a
+ /// required property) or if initialization fails.
+ ///
+ public void AfterPropertiesSet()
+ {
+ if (scheduler == null)
+ {
+ if (schedulerName != null)
+ {
+ scheduler = FindScheduler(schedulerName);
+ }
+ else
+ {
+ throw new InvalidOperationException("No Scheduler specified");
+ }
+ }
+ RegisterListeners();
+ RegisterJobsAndTriggers();
+ }
+
+ ///
+ /// Finds the scheduler.
+ ///
+ /// Name of the scheduler.
+ ///
+ protected virtual IScheduler FindScheduler(string schedulerName)
+ {
+ if (objectFactory is IListableObjectFactory)
+ {
+ IListableObjectFactory lbf = (IListableObjectFactory) objectFactory;
+ string[] objectNames = lbf.GetObjectNamesForType(typeof(IScheduler));
+ for (int i = 0; i < objectNames.Length; i++)
+ {
+ IScheduler schedulerObject = (IScheduler)lbf.GetObject(objectNames[i]);
+ if (schedulerName.Equals(schedulerObject.SchedulerName))
+ {
+ return schedulerObject;
+ }
+ }
+ }
+ IScheduler schedulerInRepo = SchedulerRepository.Instance.Lookup(schedulerName);
+ if (schedulerInRepo == null)
+ {
+ throw new InvalidOperationException("No Scheduler named '" + schedulerName + "' found");
+ }
+ return schedulerInRepo;
+ }
+
+ }
+}
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulerFactoryObject.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulerFactoryObject.cs
new file mode 100644
index 00000000..96742dbe
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulerFactoryObject.cs
@@ -0,0 +1,871 @@
+/*
+* Copyright 2002-2010 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;
+#if QUARTZ_2_0
+using System.Linq;
+#endif
+
+using Quartz;
+using Quartz.Impl;
+using Quartz.Simpl;
+using Quartz.Spi;
+using Quartz.Util;
+
+using Spring.Context;
+using Spring.Context.Events;
+using Spring.Core.IO;
+using Spring.Data.Common;
+using Spring.Objects.Factory;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// FactoryObject that sets up a Quartz Scheduler and exposes it for object references.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// For dynamic registration of jobs at runtime, use a object reference to
+ /// this SchedulerFactoryObject to get direct access to the Quartz Scheduler
+ /// ( ). This allows you to create new jobs
+ /// and triggers, and also to control and monitor the entire Scheduler.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Juergen Hoeller
+ /// Marko Lahma (.NET)
+ ///
+ ///
+ ///
+ public class SchedulerFactoryObject : SchedulerAccessor, IFactoryObject, IObjectNameAware,
+ IApplicationContextAware, IApplicationEventListener, IInitializingObject, IDisposable
+ {
+ ///
+ /// Default thread count to be set to thread pool.
+ ///
+ public const int DEFAULT_THREAD_COUNT = 10;
+
+ ///
+ /// Property name for thread count in thread pool.
+ ///
+ public const string PROP_THREAD_COUNT = "quartz.threadPool.threadCount";
+
+ [ThreadStatic]
+ private static IDbProvider configTimeDbProvider;
+
+ [ThreadStatic]
+ private static ITaskExecutor configTimeTaskExecutor;
+
+ ///
+ /// Return the IDbProvider for the currently configured Quartz Scheduler,
+ /// to be used by LocalDataSourceJobStore.
+ ///
+ ///
+ /// This instance will be set before initialization of the corresponding
+ /// Scheduler, and reset immediately afterwards. It is thus only available
+ /// during configuration.
+ ///
+ ///
+ ///
+ public static IDbProvider ConfigTimeDbProvider
+ {
+ get { return configTimeDbProvider; }
+ }
+
+ ///
+ /// Return the TaskExecutor for the currently configured Quartz Scheduler,
+ /// to be used by LocalTaskExecutorThreadPool.
+ ///
+ ///
+ /// This instance will be set before initialization of the corresponding
+ /// Scheduler, and reset immediately afterwards. It is thus only available
+ /// during configuration.
+ ///
+ 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;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SchedulerFactoryObject()
+ {
+ schedulerFactoryType = typeof (StdSchedulerFactory);
+ }
+
+
+ ///
+ /// Set the Quartz SchedulerFactory implementation to use.
+ ///
+ ///
+ /// Default is StdSchedulerFactory, reading in the standard
+ /// quartz.properties from Quartz' dll. To use custom Quartz
+ /// properties, specify "configLocation" or "quartzProperties".
+ ///
+ /// The scheduler factory class.
+ ///
+ ///
+ ///
+ public virtual Type SchedulerFactoryType
+ {
+ set
+ {
+ if (value == null || !typeof (ISchedulerFactory).IsAssignableFrom(value))
+ {
+ throw new ArgumentException("schedulerFactoryType must implement [Quartz.ISchedulerFactory]");
+ }
+ schedulerFactoryType = value;
+ }
+ }
+
+ ///
+ /// Set the name of the Scheduler to fetch from the SchedulerFactory.
+ /// If not specified, the default Scheduler will be used.
+ ///
+ /// The name of the scheduler.
+ ///
+ ///
+ public virtual string SchedulerName
+ {
+ set { schedulerName = value; }
+ }
+
+ ///
+ /// Set the location of the Quartz properties config file, for example
+ /// as assembly resource "assembly:quartz.properties".
+ ///
+ ///
+ /// Note: Can be omitted when all necessary properties are specified
+ /// locally via this object, or when relying on Quartz' default configuration.
+ ///
+ ///
+ public virtual IResource ConfigLocation
+ {
+ set { configLocation = value; }
+ }
+
+ ///
+ /// Set Quartz properties, like "quartz.threadPool.type".
+ ///
+ ///
+ /// Can be used to override values in a Quartz properties config file,
+ /// or to specify all necessary properties locally.
+ ///
+ ///
+ public virtual IDictionary QuartzProperties
+ {
+ set { quartzProperties = value; }
+ }
+
+ ///
+ /// Set the Spring TaskExecutor to use as Quartz backend.
+ /// Exposed as thread pool through the Quartz SPI.
+ ///
+ ///
+ /// By default, a Quartz SimpleThreadPool will be used, configured through
+ /// the corresponding Quartz properties.
+ ///
+ /// The task executor.
+ ///
+ ///
+ public virtual ITaskExecutor TaskExecutor
+ {
+ set { taskExecutor = value; }
+ }
+
+ ///
+ /// Register objects in the Scheduler context via a given Map.
+ /// These objects will be available to any Job that runs in this Scheduler.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Map with string keys and any objects as
+ /// values (for example Spring-managed objects)
+ ///
+ ///
+ public virtual IDictionary SchedulerContextAsMap
+ {
+ set { schedulerContextMap = value; }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// The application context scheduler context key.
+ ///
+ public virtual string ApplicationContextSchedulerContextKey
+ {
+ set { applicationContextSchedulerContextKey = value; }
+ }
+
+ ///
+ /// Set the Quartz JobFactory to use for this Scheduler.
+ ///
+ ///
+ ///
+ /// Default is Spring's , which supports
+ /// standard Quartz instances. Note that this default only applies
+ /// to a local Scheduler, not to a RemoteScheduler (where setting
+ /// a custom JobFactory is not supported by Quartz).
+ ///
+ ///
+ /// Specify an instance of Spring's here
+ /// (typically as an inner object definition) to automatically populate a job's
+ /// object properties from the specified job data map and scheduler context.
+ ///
+ ///
+ ///
+ ///
+ public virtual IJobFactory JobFactory
+ {
+ set
+ {
+ jobFactory = value;
+ jobFactorySet = true;
+ }
+ }
+
+ ///
+ /// Set whether to expose the Spring-managed instance in the
+ /// Quartz . Default is "false", since the Spring-managed
+ /// Scheduler is usually exclusively intended for access within the Spring context.
+ ///
+ ///
+ /// 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.
+ ///
+ public virtual bool ExposeSchedulerInRepository
+ {
+ set { exposeSchedulerInRepository = value; }
+ }
+
+ ///
+ /// Set whether to automatically start the scheduler after initialization.
+ /// Default is "true"; set this to "false" to allow for manual startup.
+ ///
+ public virtual bool AutoStartup
+ {
+ set { autoStartup = value; }
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// Setting this to 10 or 20 seconds makes sense if no jobs
+ /// should be run before the entire application has started up.
+ ///
+ public virtual TimeSpan StartupDelay
+ {
+ set { startupDelay = value; }
+ }
+
+ ///
+ /// Set whether to wait for running jobs to complete on Shutdown.
+ /// Default is "false".
+ ///
+ ///
+ /// true if [wait for jobs to complete on Shutdown]; otherwise, false .
+ ///
+ ///
+ public virtual bool WaitForJobsToCompleteOnShutdown
+ {
+ set { waitForJobsToCompleteOnShutdown = value; }
+ }
+
+ ///
+ /// Set the default DbProvider to be used by the Scheduler. If set,
+ /// this will override corresponding settings in Quartz properties.
+ ///
+ ///
+ ///
+ /// Note: If this is set, the Quartz settings should not define
+ /// a job store "dataSource" to avoid meaningless double configuration.
+ ///
+ ///
+ /// 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).
+ ///
+ ///
+ ///
+ ///
+ public IDbProvider DbProvider
+ {
+ set { dbProvider = value; }
+ }
+
+ ///
+ /// Set the name of the object in the object factory that created this object.
+ ///
+ /// The name of the object in the factory.
+ ///
+ ///
+ /// Invoked after population of normal object properties but before an init
+ /// callback like 's
+ ///
+ /// method or a custom init-method.
+ ///
+ ///
+ public string ObjectName
+ {
+ set
+ {
+ if (schedulerName == null)
+ {
+ schedulerName = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether this is running.
+ ///
+ /// true if running; otherwise, false .
+ public virtual bool Running
+ {
+ get
+ {
+ if (scheduler != null)
+ {
+ try
+ {
+ return !scheduler.InStandbyMode;
+ }
+ catch (SchedulerException)
+ {
+ return false;
+ }
+ }
+ return false;
+ }
+ }
+
+ #region IApplicationContextAware Members
+
+ ///
+ /// Sets the that this
+ /// object runs in.
+ ///
+ ///
+ ///
+ ///
+ /// Normally this call will be used to initialize the object.
+ ///
+ ///
+ /// Invoked after population of normal object properties but before an
+ /// init callback such as
+ /// 's
+ ///
+ /// or a custom init-method. Invoked after the setting of any
+ /// 's
+ ///
+ /// property.
+ ///
+ ///
+ ///
+ /// In the case of application context initialization errors.
+ ///
+ ///
+ /// If thrown by any application context methods.
+ ///
+ ///
+ public virtual IApplicationContext ApplicationContext
+ {
+ set { applicationContext = value; }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ ///
+ /// Shut down the Quartz scheduler on object factory Shutdown,
+ /// stopping all scheduled jobs.
+ ///
+ public virtual void Dispose()
+ {
+ logger.Info("Shutting down Quartz Scheduler");
+ scheduler.Shutdown(waitForJobsToCompleteOnShutdown);
+ }
+
+ #endregion
+
+ ///
+ /// Template method that determines the Scheduler to operate on.
+ /// To be implemented by subclasses.
+ ///
+ ///
+ protected override IScheduler GetScheduler()
+ {
+ return scheduler;
+ }
+
+ #region IFactoryObject Members
+
+ ///
+ /// Return an instance (possibly shared or independent) of the object
+ /// managed by this factory.
+ ///
+ ///
+ /// An instance (possibly shared or independent) of the object managed by
+ /// this factory.
+ ///
+ ///
+ ///
+ /// If this method is being called in the context of an enclosing IoC container and
+ /// returns , the IoC container will consider this factory
+ /// object as not being fully initialized and throw a corresponding (and most
+ /// probably fatal) exception.
+ ///
+ ///
+ public virtual object GetObject()
+ {
+ return scheduler;
+ }
+
+ ///
+ /// Return the of object that this
+ /// creates, or
+ /// if not known in advance.
+ ///
+ ///
+ public virtual Type ObjectType
+ {
+ get { return (scheduler != null) ? scheduler.GetType() : typeof (IScheduler); }
+ }
+
+ ///
+ /// Is the object managed by this factory a singleton or a prototype?
+ ///
+ ///
+ public virtual bool IsSingleton
+ {
+ get { return true; }
+ }
+
+ #endregion
+
+ //---------------------------------------------------------------------
+ // Implementation of IInitializingObject interface
+ //---------------------------------------------------------------------
+
+ #region IInitializingObject Members
+
+ ///
+ /// Invoked by an
+ /// after it has injected all of an object's dependencies.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Please do consult the class level documentation for the
+ /// interface for a
+ /// description of exactly when this method is invoked. In
+ /// particular, it is worth noting that the
+ ///
+ /// and
+ /// callbacks will have been invoked prior to this method being
+ /// called.
+ ///
+ ///
+ ///
+ /// In the event of misconfiguration (such as the failure to set a
+ /// required property) or if initialization fails.
+ ///
+ public virtual void AfterPropertiesSet()
+ {
+ // Create SchedulerFactory instance.
+#if QUARTZ_2_0
+ ISchedulerFactory schedulerFactory = ObjectUtils.InstantiateType(schedulerFactoryType);
+#else
+ ISchedulerFactory schedulerFactory = (ISchedulerFactory) ObjectUtils.InstantiateType(schedulerFactoryType);
+#endif
+
+ 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();
+ }
+
+ #endregion
+
+ ///
+ /// Load and/or apply Quartz properties to the given SchedulerFactory.
+ ///
+ /// the SchedulerFactory to Initialize
+ 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);
+ }
+
+ ///
+ /// Merges the properties into map. This effectively also
+ /// overwrites existing properties with same key in map.
+ ///
+ /// The properties to merge into given map.
+ /// The map to merge to.
+ protected virtual void MergePropertiesIntoMap(IDictionary properties, NameValueCollection map)
+ {
+ foreach (string key in properties.Keys)
+ {
+ map[key] = (string) properties[key];
+ }
+ }
+
+
+ ///
+ /// Create the Scheduler instance for the given factory and scheduler name.
+ /// Called by afterPropertiesSet.
+ ///
+ ///
+ /// Default implementation invokes SchedulerFactory's GetScheduler
+ /// method. Can be overridden for custom Scheduler creation.
+ ///
+ /// the factory to create the Scheduler with
+ /// the name of the scheduler to create
+ /// the Scheduler instance
+ ///
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Expose the specified context attributes and/or the current
+ /// IApplicationContext in the Quartz SchedulerContext.
+ ///
+ private void PopulateSchedulerContext()
+ {
+ // Put specified objects into Scheduler context.
+ if (schedulerContextMap != null)
+ {
+#if QUARTZ_2_0
+ var dictionary = schedulerContextMap.Cast().ToDictionary(entry => entry.Key.ToString(), entry => entry.Value);
+ scheduler.Context.PutAll(dictionary);
+#else
+ scheduler.Context.PutAll(schedulerContextMap);
+#endif
+ }
+
+ // 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);
+ }
+ }
+
+
+ ///
+ /// Start the Quartz Scheduler, respecting the "startDelay" setting.
+ ///
+ /// the Scheduler to start
+ /// the time span to wait before starting
+ /// the Scheduler asynchronously
+ 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
+ //---------------------------------------------------------------------
+
+ ///
+ /// Starts this instance.
+ ///
+ public virtual void Start()
+ {
+ if (scheduler != null)
+ {
+ try
+ {
+ scheduler.Start();
+ }
+ catch (SchedulerException ex)
+ {
+ throw new SchedulingException("Could not start Quartz Scheduler", ex);
+ }
+ }
+ }
+
+ ///
+ /// Stops this instance.
+ ///
+ public virtual void Stop()
+ {
+ if (scheduler != null)
+ {
+ try
+ {
+ scheduler.Standby();
+ }
+ catch (SchedulerException ex)
+ {
+ throw new SchedulingException("Could not stop Quartz Scheduler", ex);
+ }
+ }
+ }
+
+ ///
+ /// Handles the application context's refresh event and starts the scheduler.
+ ///
+ public void HandleApplicationEvent(object sender, ApplicationEventArgs e)
+ {
+ // auto-start Scheduler if demanded
+ if (e is ContextRefreshedEventArgs && autoStartup)
+ {
+ try
+ {
+ StartScheduler(scheduler, startupDelay);
+ }
+ catch (SchedulerException ex)
+ {
+ throw new ObjectInitializationException("failed to auto-start scheduler", ex);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulingException.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulingException.cs
new file mode 100644
index 00000000..eb12e030
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SchedulingException.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Generic scheduling exception.
+ ///
+ public class SchedulingException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message.
+ public SchedulingException(string message) : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message.
+ /// The original exception.
+ public SchedulingException(string message, Exception ex) : base(message, ex)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SimpleThreadPoolTaskExecutor.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SimpleThreadPoolTaskExecutor.cs
new file mode 100644
index 00000000..b1cd9f67
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SimpleThreadPoolTaskExecutor.cs
@@ -0,0 +1,127 @@
+/*
+* Copyright 2002-2010 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.Threading;
+
+using Quartz;
+using Quartz.Simpl;
+using Spring.Objects.Factory;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Subclass of Quartz's SimpleThreadPool that implements Spring's
+ /// TaskExecutor interface and listens to Spring lifecycle callbacks.
+ ///
+ /// Juergen Hoeller
+ ///
+ ///
+ ///
+ public class SimpleThreadPoolTaskExecutor : SimpleThreadPool, ISchedulingTaskExecutor, IInitializingObject, IDisposable
+ {
+ private bool waitForJobsToCompleteOnShutdown = false;
+
+ ///
+ /// Set whether to wait for running jobs to complete on Shutdown.
+ /// Default is "false".
+ ///
+ ///
+ /// true if [wait for jobs to complete on shutdown]; otherwise, false .
+ ///
+ ///
+ public virtual bool WaitForJobsToCompleteOnShutdown
+ {
+ set { waitForJobsToCompleteOnShutdown = value; }
+ }
+
+ ///
+ /// Invoked by an
+ /// after it has injected all of an object's dependencies.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Please do consult the class level documentation for the
+ /// interface for a
+ /// description of exactly when this method is invoked. In
+ /// particular, it is worth noting that the
+ ///
+ /// and
+ /// callbacks will have been invoked prior to this method being
+ /// called.
+ ///
+ ///
+ ///
+ /// In the event of misconfiguration (such as the failure to set a
+ /// required property) or if initialization fails.
+ ///
+ public virtual void AfterPropertiesSet()
+ {
+ Initialize();
+ }
+
+ ///
+ /// Executes the specified task.
+ ///
+ /// The task.
+ public virtual void Execute(ThreadStart task)
+ {
+ if (task == null)
+ {
+ throw new ArgumentException("Runnable must not be null", "task");
+ }
+ if (!RunInThread(new ThreadRunnableDelegate(task)))
+ {
+ throw new SchedulingException("Quartz SimpleThreadPool already shut down");
+ }
+ }
+
+ /// This task executor prefers short-lived work units.
+ public virtual bool PrefersShortLivedTasks
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public virtual void Dispose()
+ {
+ Shutdown(waitForJobsToCompleteOnShutdown);
+ }
+
+ internal class ThreadRunnableDelegate : IThreadRunnable
+ {
+ private ThreadStart ts;
+
+
+ public ThreadRunnableDelegate(ThreadStart ts)
+ {
+ this.ts = ts;
+ }
+
+ public void Run()
+ {
+ ts.Invoke();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SpringDbProviderAdapter.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SpringDbProviderAdapter.cs
new file mode 100644
index 00000000..e83a3887
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SpringDbProviderAdapter.cs
@@ -0,0 +1,291 @@
+ /*
+ * Copyright 2002-2010 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.Data;
+using System.Reflection;
+
+using Quartz.Impl.AdoJobStore;
+using Quartz.Impl.AdoJobStore.Common;
+
+using IDbMetadata=Spring.Data.Common.IDbMetadata;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Adapts Spring's to Quartz's
+ /// .
+ ///
+ public class SpringDbProviderAdapter : IDbProvider
+ {
+ private readonly Data.Common.IDbProvider dbProvider;
+ private readonly DbMetadata metadata;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Spring db provider.
+ public SpringDbProviderAdapter(Data.Common.IDbProvider dbProvider)
+ {
+ this.dbProvider = dbProvider;
+ metadata = new SpringMetadataAdapter(dbProvider.DbMetadata);
+ }
+
+
+ ///
+ /// Creates the command.
+ ///
+ ///
+ public IDbCommand CreateCommand()
+ {
+ return dbProvider.CreateCommand();
+ }
+
+ ///
+ /// Creates the command builder.
+ ///
+ ///
+ public object CreateCommandBuilder()
+ {
+ return dbProvider.CreateCommandBuilder();
+ }
+
+ ///
+ /// Creates the connection.
+ ///
+ ///
+ public IDbConnection CreateConnection()
+ {
+ return dbProvider.CreateConnection();
+ }
+
+ ///
+ /// Creates the parameter.
+ ///
+ ///
+ public IDbDataParameter CreateParameter()
+ {
+ return dbProvider.CreateParameter();
+ }
+
+ ///
+ /// Shutdowns this instance.
+ ///
+ public void Shutdown()
+ {
+ // no-op
+ }
+
+ ///
+ /// Gets or sets the connection string.
+ ///
+ /// The connection string.
+ public string ConnectionString
+ {
+ get { return dbProvider.ConnectionString; }
+ set { dbProvider.ConnectionString = value; }
+ }
+
+ ///
+ /// Gets the metadata.
+ ///
+ /// The metadata.
+ public DbMetadata Metadata
+ {
+ get { return metadata; }
+ }
+ }
+
+ ///
+ /// Helper class to map between Quartz and Spring DB metadata.
+ ///
+ public class SpringMetadataAdapter : DbMetadata
+ {
+ private readonly IDbMetadata metadata;
+ private readonly Enum dbTypeBinary;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The metadata to wrap and adapt.
+ public SpringMetadataAdapter(IDbMetadata metadata)
+ {
+ this.metadata = metadata;
+ // determine correct binary enum type
+ Type parameterType = metadata.ParameterDbType;
+ FieldInfo blobField = parameterType.GetField("Blob");
+ FieldInfo imageField = parameterType.GetField("Image");
+ if (blobField != null)
+ {
+ // uses Blob, for example Oracle
+ dbTypeBinary = (Enum) blobField.GetValue(Activator.CreateInstance(parameterType));
+ }
+ else if (imageField != null)
+ {
+ // uses Image, SQL Server
+ dbTypeBinary = (Enum) imageField.GetValue(Activator.CreateInstance(parameterType));
+ }
+ else
+ {
+ // use standard binary type
+ dbTypeBinary = DbType.Binary;
+ }
+
+ }
+
+ ///
+ /// Gets or sets the name of the product.
+ ///
+ /// The name of the product.
+ public override string ProductName
+ {
+ get { return metadata.ProductName; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the type of the connection.
+ ///
+ /// The type of the connection.
+ public override Type ConnectionType
+ {
+ get { return metadata.ConnectionType; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the type of the command.
+ ///
+ /// The type of the command.
+ public override Type CommandType
+ {
+ get { return metadata.CommandType; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the type of the parameter.
+ ///
+ /// The type of the parameter.
+ public override Type ParameterType
+ {
+ get { return metadata.ParameterType; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the type of the command builder.
+ ///
+ /// The type of the command builder.
+ public override Type CommandBuilderType
+ {
+ get { return metadata.CommandBuilderType; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the command builder derive parameters method.
+ ///
+ /// The command builder derive parameters method.
+ public override MethodInfo CommandBuilderDeriveParametersMethod
+ {
+ get { return metadata.CommandBuilderDeriveParametersMethod; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the parameter name prefix.
+ ///
+ /// The parameter name prefix.
+ public override string ParameterNamePrefix
+ {
+ get { return metadata.ParameterNamePrefix; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the type of the exception.
+ ///
+ /// The type of the exception.
+ public override Type ExceptionType
+ {
+ get { return metadata.ExceptionType; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether [bind by name].
+ ///
+ /// true if [bind by name]; otherwise, false .
+ public override bool BindByName
+ {
+ get { return metadata.BindByName; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the type of the parameter db.
+ ///
+ /// The type of the parameter db.
+ public override Type ParameterDbType
+ {
+ get { return metadata.ParameterDbType; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the parameter db type property.
+ ///
+ /// The parameter db type property.
+ public override PropertyInfo ParameterDbTypeProperty
+ {
+ get { return metadata.ParameterDbTypeProperty; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the parameter is nullable property.
+ ///
+ /// The parameter is nullable property.
+ public override PropertyInfo ParameterIsNullableProperty
+ {
+ get { return metadata.ParameterIsNullableProperty; }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the type of the db binary.
+ ///
+ /// The type of the db binary.
+ public override Enum DbBinaryType
+ {
+ get { return dbTypeBinary; }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether [use parameter name prefix in parameter collection].
+ ///
+ ///
+ /// true if [use parameter name prefix in parameter collection]; otherwise, false .
+ ///
+ public override bool UseParameterNamePrefixInParameterCollection
+ {
+ get { return metadata.UseParameterNamePrefixInParameterCollection; }
+ set { throw new NotSupportedException(); }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SpringObjectJobFactory.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SpringObjectJobFactory.cs
new file mode 100644
index 00000000..87efe3e3
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/SpringObjectJobFactory.cs
@@ -0,0 +1,120 @@
+/*
+* Copyright 2002-2010 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 Quartz;
+using Quartz.Spi;
+using Spring.Objects;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Subclass of AdaptableJobFactory that also supports Spring-style
+ /// dependency injection on object properties. This is essentially the direct
+ /// equivalent of Spring's QuartzJobObject in the shape of a
+ /// Quartz JobFactory.
+ ///
+ ///
+ /// Applies scheduler context, job data map and trigger data map entries
+ /// as object property values. If no matching object property is found, the entry
+ /// is by default simply ignored. This is analogous to QuartzJobObject's behavior.
+ ///
+ /// Juergen Hoeller
+ ///
+ ///
+ public class SpringObjectJobFactory : AdaptableJobFactory, ISchedulerContextAware
+ {
+ private string[] ignoredUnknownProperties;
+ private SchedulerContext schedulerContext;
+
+ ///
+ /// Specify the unknown properties (not found in the object) that should be ignored.
+ ///
+ ///
+ /// Default is null, indicating that all unknown properties
+ /// should be ignored. Specify an empty array to throw an exception in case
+ /// of any unknown properties, or a list of property names that should be
+ /// ignored if there is no corresponding property found on the particular
+ /// job class (all other unknown properties will still trigger an exception).
+ ///
+ public virtual string[] IgnoredUnknownProperties
+ {
+ set { ignoredUnknownProperties = value; }
+ }
+
+ ///
+ /// Set the SchedulerContext of the current Quartz Scheduler.
+ ///
+ ///
+ ///
+ public virtual SchedulerContext SchedulerContext
+ {
+ set { schedulerContext = value; }
+ }
+
+
+ ///
+ /// Create the job instance, populating it with property values taken
+ /// from the scheduler context, job data map and trigger data map.
+ ///
+ protected override object CreateJobInstance(TriggerFiredBundle bundle)
+ {
+ ObjectWrapper ow = new ObjectWrapper(bundle.JobDetail.JobType);
+ if (IsEligibleForPropertyPopulation(ow.WrappedInstance))
+ {
+ MutablePropertyValues pvs = new MutablePropertyValues();
+ if (schedulerContext != null)
+ {
+ pvs.AddAll(schedulerContext);
+ }
+ pvs.AddAll(bundle.JobDetail.JobDataMap);
+ pvs.AddAll(bundle.Trigger.JobDataMap);
+ if (ignoredUnknownProperties != null)
+ {
+ for (int i = 0; i < ignoredUnknownProperties.Length; i++)
+ {
+ string propName = ignoredUnknownProperties[i];
+ if (pvs.Contains(propName))
+ {
+ pvs.Remove(propName);
+ }
+ }
+ ow.SetPropertyValues(pvs);
+ }
+ else
+ {
+ ow.SetPropertyValues(pvs, true);
+ }
+ }
+ return ow.WrappedInstance;
+ }
+
+ ///
+ /// Return whether the given job object is eligible for having
+ /// its object properties populated.
+ ///
+ /// The default implementation ignores QuartzJobObject instances,
+ /// which will inject object properties themselves.
+ ///
+ ///
+ ///
+ /// The job object to introspect.
+ ///
+ ///
+ protected virtual bool IsEligibleForPropertyPopulation(object jobObject)
+ {
+ return (!(jobObject is QuartzJobObject));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/TaskRejectedException.cs b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/TaskRejectedException.cs
new file mode 100644
index 00000000..4ac25434
--- /dev/null
+++ b/src/Spring/Spring.Scheduling.Quartz20/Scheduling/Quartz/TaskRejectedException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Spring.Scheduling
+{
+ ///
+ /// Summary description for TaskRejectedException.
+ ///
+ public class TaskRejectedException : ApplicationException
+ {
+ }
+}
diff --git a/src/Spring/Spring.Scheduling.Quartz20/Spring.Scheduling.Quartz20.2010.csproj b/src/Spring/Spring.Scheduling.Quartz20/Spring.Scheduling.Quartz20.2010.csproj
index e94b9e84..d0595f83 100644
--- a/src/Spring/Spring.Scheduling.Quartz20/Spring.Scheduling.Quartz20.2010.csproj
+++ b/src/Spring/Spring.Scheduling.Quartz20/Spring.Scheduling.Quartz20.2010.csproj
@@ -51,69 +51,31 @@
-
- JobMethodInvocationFailedException.cs
-
-
- Scheduling\Quartz\AdaptableJobFactory.cs
-
-
- Scheduling\Quartz\DelegatingJob.cs
-
-
- Scheduling\Quartz\IJobDetailAwareTrigger.cs
-
-
- Scheduling\Quartz\ISchedulerContextAware.cs
-
-
- Scheduling\Quartz\ISchedulingTaskExecutor.cs
-
-
- Scheduling\Quartz\ITaskExecutor.cs
-
-
- Scheduling\Quartz\LocalDataSourceJobStore.cs
-
-
- Scheduling\Quartz\LocalTaskExecutorThreadPool.cs
-
-
- Scheduling\Quartz\MethodInvokingJob.cs
-
-
- Scheduling\Quartz\MethodInvokingRunnable.cs
-
-
- Scheduling\Quartz\QuartzJobObject.cs
-
-
- Scheduling\Quartz\SchedulerAccessorObject.cs
-
-
- Scheduling\Quartz\SchedulerFactoryObject.cs
-
-
- Scheduling\Quartz\SchedulingException.cs
-
-
- Scheduling\Quartz\SimpleThreadPoolTaskExecutor.cs
-
-
- Scheduling\Quartz\SpringDbProviderAdapter.cs
-
-
- Scheduling\Quartz\SpringObjectJobFactory.cs
-
-
- Scheduling\Quartz\TaskRejectedException.cs
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/QuartzCompilerOptionsTests.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/QuartzCompilerOptionsTests.cs
new file mode 100644
index 00000000..8bc4feaf
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/QuartzCompilerOptionsTests.cs
@@ -0,0 +1,43 @@
+#region License
+
+/*
+ * Copyright 2004-2009 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.
+ */
+
+#endregion
+
+#region Imports
+
+using System.Reflection;
+using NUnit.Framework;
+using Spring.Scheduling.Quartz;
+
+#endregion
+
+namespace Spring
+{
+ /// Test that the assembly is built with the correct DebugAttributes in release and debug builds.
+ ///
+ /// Mark Pollack
+ [TestFixture]
+ public sealed class QuartzCompilerOptionTests : CompilerOptionsTests
+ {
+ [TestFixtureSetUp]
+ public void FixtureSetUp()
+ {
+ AssemblyToCheck = Assembly.GetAssembly(typeof (SchedulingException));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/AdaptableJobFactoryTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/AdaptableJobFactoryTest.cs
new file mode 100644
index 00000000..617bf92b
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/AdaptableJobFactoryTest.cs
@@ -0,0 +1,106 @@
+/*
+* Copyright 2002-2010 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 NUnit.Framework;
+using Quartz;
+using Quartz.Job;
+using Quartz.Spi;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Tests for .
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class AdaptableJobFactoryTest
+ {
+ private AdaptableJobFactory jobFactory;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ jobFactory = new AdaptableJobFactory();
+ }
+
+ ///
+ /// Tests job creation.
+ ///
+ [Test]
+ public void TestNewJob_IncompatibleJob()
+ {
+ try
+ {
+ // this actually fails already in Quartz level
+ TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof (object));
+#if QUARTZ_2_0
+ jobFactory.NewJob(bundle, null);
+#else
+ jobFactory.NewJob(bundle);
+#endif
+ Assert.Fail("Created job which was not an IJob");
+ }
+ catch (ArgumentException)
+ {
+ // ok
+ }
+ catch (SchedulerException)
+ {
+ // ok
+ }
+ catch (Exception)
+ {
+ Assert.Fail("Got exception that was not instance of SchedulerException or ArgumentException");
+ }
+ }
+
+ ///
+ /// Tests job creation.
+ ///
+ [Test]
+ public void TestNewJob_ThreadStartJob()
+ {
+ // TODO ThreadStart is not the way to go
+ }
+
+ ///
+ /// Tests job creation.
+ ///
+ [Test]
+ public void TestNewJob_NormalIJob()
+ {
+ TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof (NoOpJob));
+#if QUARTZ_2_0
+ IJob job = jobFactory.NewJob(bundle, null);
+#else
+ IJob job = jobFactory.NewJob(bundle);
+#endif
+
+ Assert.IsNotNull(job, "Returned job was null");
+ }
+ }
+
+ internal class NoOpThreadStartJob : NoOpJob
+ {
+ public void Execute()
+ {
+ Execute(null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/CronTriggerObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/CronTriggerObjectTest.cs
new file mode 100644
index 00000000..4ceace1f
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/CronTriggerObjectTest.cs
@@ -0,0 +1,118 @@
+/*
+* Copyright 2002-2010 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.Generic;
+using NUnit.Framework;
+
+using Quartz;
+using Quartz.Job;
+
+#if QUARTZ_2_0
+using JobDetail = Quartz.Impl.JobDetailImpl;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Tests for .
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class CronTriggerObjectTest : TriggerObjectTest
+ {
+ private CronTriggerObject cronTrigger;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ cronTrigger = new CronTriggerObject();
+ cronTrigger.ObjectName = TRIGGER_NAME;
+ Trigger = cronTrigger;
+ }
+
+ ///
+ /// Tests all possible misfire instructions for cron trigger
+ /// from strings to int.
+ ///
+ [Test]
+ public void TestMisfireInstructionNames()
+ {
+ string[] names = new string[] { "DoNothing", "FireOnceNow", "SmartPolicy" };
+ foreach (string name in names)
+ {
+ cronTrigger.MisfireInstructionName = name;
+ }
+ }
+
+ ///
+ /// Tests that JobDetail defaults values as expected in AfterPropertiesSet.
+ ///
+ [Test]
+ public override void TestAfterPropertiesSet_JobDetailGiven()
+ {
+ const string jobName = "jobName";
+ const string jobGroup = "jobGroup";
+ JobDetail jd = new JobDetail(jobName, jobGroup, typeof (NoOpJob));
+ cronTrigger.JobDetail = jd;
+ cronTrigger.AfterPropertiesSet();
+ base.TestAfterPropertiesSet_JobDetailGiven();
+ Assert.AreSame(jd, cronTrigger.JobDetail, "job details weren't same");
+ }
+
+ ///
+ /// Tests that JobDetail maps job data map as expected.
+ ///
+ [Test]
+ public void TestJobDataAsMap()
+ {
+#if QUARTZ_2_0
+ IDictionary data = new Dictionary();
+#else
+ Hashtable data = new Hashtable();
+#endif
+ data["foo"] = "bar";
+ data["number"] = 123;
+ cronTrigger.JobDataAsMap = data;
+ CollectionAssert.AreEquivalent(data, cronTrigger.JobDataMap, "Data differed");
+ }
+
+ ///
+ /// Tests that StartDelay is respected.
+ ///
+ [Test]
+ public void TestStartDelay()
+ {
+ TimeSpan expectedDelay = TimeSpan.FromMinutes(10);
+ cronTrigger.StartDelay = expectedDelay;
+ Assert.AreEqual(expectedDelay, cronTrigger.StartDelay);
+
+ cronTrigger.AfterPropertiesSet();
+ DateTime now = DateTime.UtcNow;
+ TimeSpan delay = cronTrigger.StartTimeUtc - now;
+
+ // check roughly
+ Assert.IsTrue(delay > TimeSpan.FromMinutes(9).Add(TimeSpan.FromSeconds(55)));
+ Assert.IsTrue(delay < TimeSpan.FromMinutes(10).Add(TimeSpan.FromSeconds(5)));
+ }
+
+ }
+
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/JobDetailObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/JobDetailObjectTest.cs
new file mode 100644
index 00000000..f83c6bfe
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/JobDetailObjectTest.cs
@@ -0,0 +1,165 @@
+/*
+* Copyright 2002-2010 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 NUnit.Framework;
+
+using Quartz;
+using Quartz.Job;
+
+using Spring.Context.Support;
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Tests for .
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class JobDetailObjectTest
+ {
+ private JobDetailObject jobDetail;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ jobDetail = new JobDetailObject();
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void TestJobType_Null()
+ {
+ jobDetail.JobType = null;
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ public void TestJobType_NonIJob()
+ {
+ jobDetail.JobType = typeof(object);
+ Assert.AreEqual(typeof(object), jobDetail.JobType, "JobDetail did not create same type as expected");
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ public void TestJobType_IJob()
+ {
+ Type CORRECT_IJOB = typeof (NoOpJob);
+ jobDetail.JobType = CORRECT_IJOB;
+ Assert.AreEqual(jobDetail.JobType, CORRECT_IJOB, "JobDetail did not register correct job type");
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void TestJobDataAsMap_Null()
+ {
+ jobDetail.JobDataAsMap = null;
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ public void TestJobDataAsMap_ProperValues()
+ {
+ IDictionary values = new Hashtable();
+ values["baz"] = "foo";
+ values["foo"] = 123;
+ values["bar"] = null;
+ jobDetail.JobDataAsMap = values;
+ Assert.AreEqual(values.Count, jobDetail.JobDataMap.Count, "Data of inequal size");
+ CollectionAssert.AreEquivalent(values.Keys, jobDetail.JobDataMap.Keys, "JobDataMap values not equal");
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_Defaults()
+ {
+ const string objectName = "springJobDetailObject";
+ jobDetail.ObjectName = objectName;
+ jobDetail.Group = null;
+ jobDetail.AfterPropertiesSet();
+ Assert.AreEqual(SchedulerConstants.DefaultGroup, jobDetail.Group, "Groups differ");
+ Assert.AreEqual(objectName, jobDetail.Name, "Names differ");
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_CustomNameAndGroup()
+ {
+ const string objectName = "springJobDetailObject";
+ const string jobDetailName = "jobDetailName";
+ const string jobDetailGroup = "jobDetailGroup";
+ jobDetail.ObjectName = objectName;
+ jobDetail.Name = jobDetailName;
+ jobDetail.Group = jobDetailGroup;
+ jobDetail.AfterPropertiesSet();
+ Assert.AreEqual(jobDetailGroup, jobDetail.Group, "Groups differ");
+ Assert.AreEqual(jobDetailName, jobDetail.Name, "Names differ");
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_ApplicationContextJobDataKeySetWithApplicationContext()
+ {
+ const string objectName = "springJobDetailObject";
+ jobDetail.ObjectName = objectName;
+ StaticApplicationContext ctx = new StaticApplicationContext();
+ jobDetail.ApplicationContext = ctx;
+ string key = "applicationContextJobDataKey";
+ jobDetail.ApplicationContextJobDataKey = key;
+ jobDetail.AfterPropertiesSet();
+ Assert.AreSame(ctx, jobDetail.ApplicationContext, "ApplicationContext was not set correctly");
+ Assert.AreSame(ctx, jobDetail.JobDataMap[key], "ApplicationContext was not set to job data map");
+ }
+
+ ///
+ /// Tests job detail's property behavior.
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void TestAfterPropertiesSet_ApplicationContextJobDataKeySetWithoutApplicationContext()
+ {
+ const string objectName = "springJobDetailObject";
+ jobDetail.ObjectName = objectName;
+ jobDetail.ApplicationContextJobDataKey = "applicationContextJobDataKey";
+ jobDetail.AfterPropertiesSet();
+ }
+
+ }
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/MethodInvokingJobDetailFactoryObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/MethodInvokingJobDetailFactoryObjectTest.cs
new file mode 100644
index 00000000..bc34b359
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/MethodInvokingJobDetailFactoryObjectTest.cs
@@ -0,0 +1,95 @@
+/*
+* Copyright 2002-2010 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 NUnit.Framework;
+
+using Quartz;
+
+#if QUARTZ_2_0
+using JobDetail = Quartz.Impl.JobDetailImpl;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Unit tests for MethodInvokingJobDetailFactoryObject.
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class MethodInvokingJobDetailFactoryObjectTest
+ {
+ private const string FACTORY_NAME = "springObjectFactory";
+ private MethodInvokingJobDetailFactoryObject factory;
+
+ ///
+ /// Setup for the test.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ factory = new MethodInvokingJobDetailFactoryObject();
+ factory.ObjectName = FACTORY_NAME;
+ factory.TargetMethod = "Invoke";
+ factory.TargetObject = new InvocationCountingJob();
+ }
+
+ ///
+ /// Tests JobDetail retrieval and it's set properties.
+ ///
+ [Test]
+ public void TestGetObject_MinimalDefaults()
+ {
+ factory.AfterPropertiesSet();
+ JobDetail jd = (JobDetail) factory.GetObject();
+ Assert.IsNotNull(jd, "job detail was null");
+ Assert.AreEqual(FACTORY_NAME, jd.Name, "job name did not default to factory name");
+ Assert.AreEqual(jd.JobType, typeof(MethodInvokingJob), "factory did not create method invoking job");
+ Assert.IsTrue(jd.Durable, "job was not durable");
+#if !QUARTZ_2_0
+ Assert.IsTrue(jd.Volatile, "job was not volatile");
+#endif
+ }
+
+ ///
+ /// Tests JobDetail retrieval and it's set properties.
+ ///
+ [Test]
+ public void TestGetObject_ConcurrentJob()
+ {
+ factory.Concurrent = false;
+ factory.AfterPropertiesSet();
+ JobDetail jd = (JobDetail)factory.GetObject();
+ Assert.IsNotNull(jd, "job detail was null");
+ Assert.AreEqual(jd.JobType, typeof(StatefulMethodInvokingJob), "factory did not create stateful method invoking job");
+ }
+
+#if !QUARTZ_2_0
+ ///
+ /// Tests JobDetail retrieval and it's set properties.
+ ///
+ [Test]
+ public void TestGetObject_TriggerListenersSet()
+ {
+ string[] LISTENER_NAMES = new string[] {"Foo", "Bar"};
+ factory.JobListenerNames = LISTENER_NAMES;
+ factory.AfterPropertiesSet();
+ JobDetail jd = (JobDetail)factory.GetObject();
+ CollectionAssert.AreEquivalent(LISTENER_NAMES, jd.JobListenerNames);
+ }
+#endif
+
+ }
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/MethodInvokingJobTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/MethodInvokingJobTest.cs
new file mode 100644
index 00000000..6dfedcfd
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/MethodInvokingJobTest.cs
@@ -0,0 +1,213 @@
+/*
+* Copyright 2002-2010 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.Threading;
+
+using NUnit.Framework;
+
+using Quartz;
+using Quartz.Job;
+using Quartz.Spi;
+
+using Rhino.Mocks;
+
+using Spring.Objects.Support;
+
+#if QUARTZ_2_0
+using JobExecutionContext = Quartz.Impl.JobExecutionContextImpl;
+using JobDetail = Quartz.Impl.JobDetailImpl;
+using SimpleTrigger = Quartz.Impl.Triggers.SimpleTriggerImpl;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Tests for MethodInvokingJob.
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class MethodInvokingJobTest
+ {
+ private MethodInvokingJob methodInvokingJob;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ methodInvokingJob = new MethodInvokingJob();
+ }
+
+ ///
+ /// Test method invoke via execute.
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void TestMethodInvoker_SetWithNull()
+ {
+ methodInvokingJob.MethodInvoker = null;
+ }
+
+ ///
+ /// Test method invoke via execute.
+ ///
+ [Test]
+ [ExpectedException(typeof(JobExecutionException))]
+ public void TestMethodInvocation_NullMethodInvokder()
+ {
+ methodInvokingJob.Execute(CreateMinimalJobExecutionContext());
+ }
+
+ ///
+ /// Test method invoke via execute.
+ ///
+ [Test]
+ public void TestMethodInvoker_MethodSetCorrectly()
+ {
+ InvocationCountingJob job = new InvocationCountingJob();
+ MethodInvoker mi = new MethodInvoker();
+ mi.TargetObject = job;
+ mi.TargetMethod = "Invoke";
+ mi.Prepare();
+ methodInvokingJob.MethodInvoker = mi;
+ methodInvokingJob.Execute(CreateMinimalJobExecutionContext());
+ Assert.AreEqual(1, job.CounterValue, "Job was not invoked once");
+ }
+
+ ///
+ /// Test that invocation result is set to execution context (SPRNET-1340).
+ ///
+ [Test]
+ public void TestMethodInvoker_ShouldSetResultToExecutionContext()
+ {
+ InvocationCountingJob job = new InvocationCountingJob();
+ MethodInvoker mi = new MethodInvoker();
+ mi.TargetObject = job;
+ mi.TargetMethod = "InvokeWithReturnValue";
+ mi.Prepare();
+ methodInvokingJob.MethodInvoker = mi;
+ JobExecutionContext context = CreateMinimalJobExecutionContext();
+ methodInvokingJob.Execute(context);
+
+ Assert.AreEqual(InvocationCountingJob.DefaultReturnValue, context.Result, "result value was not set to context");
+ }
+
+ ///
+ /// Test method invoke via execute.
+ ///
+ [Test]
+ public void TestMethodInvoker_MethodSetCorrectlyThrowsException()
+ {
+ InvocationCountingJob job = new InvocationCountingJob();
+ MethodInvoker mi = new MethodInvoker();
+ mi.TargetObject = job;
+ mi.TargetMethod = "InvokeAndThrowException";
+ mi.Prepare();
+ methodInvokingJob.MethodInvoker = mi;
+ try
+ {
+ methodInvokingJob.Execute(CreateMinimalJobExecutionContext());
+ Assert.Fail("Successful invoke when method threw exception");
+ }
+ catch (JobMethodInvocationFailedException)
+ {
+ // ok
+ }
+ Assert.AreEqual(1, job.CounterValue, "Job was not invoked once");
+ }
+
+ ///
+ /// Test method invoke via execute.
+ ///
+ [Test]
+ public void TestMethodInvoker_PrivateMethod()
+ {
+ InvocationCountingJob job = new InvocationCountingJob();
+ MethodInvoker mi = new MethodInvoker();
+ mi.TargetObject = job;
+ mi.TargetMethod = "PrivateMethod";
+ mi.Prepare();
+ methodInvokingJob.MethodInvoker = mi;
+ methodInvokingJob.Execute(CreateMinimalJobExecutionContext());
+ }
+
+ private static JobExecutionContext CreateMinimalJobExecutionContext()
+ {
+ MockRepository repo = new MockRepository();
+ IScheduler sched = (IScheduler) repo.DynamicMock(typeof (IScheduler));
+ JobExecutionContext ctx = new JobExecutionContext(sched, ConstructMinimalTriggerFiredBundle(), null);
+ return ctx;
+ }
+
+ private static TriggerFiredBundle ConstructMinimalTriggerFiredBundle()
+ {
+ JobDetail jd = new JobDetail("jobName", "jobGroup", typeof(NoOpJob));
+ SimpleTrigger trigger = new SimpleTrigger("triggerName", "triggerGroup");
+ TriggerFiredBundle retValue = new TriggerFiredBundle(jd, trigger, null, false, null, null, null, null);
+
+ return retValue;
+ }
+
+ }
+
+ ///
+ /// Test class for method invoker.
+ ///
+ public class InvocationCountingJob
+ {
+ private int counter;
+ internal const string DefaultReturnValue = "return value";
+
+ ///
+ /// Increments method invoke counter.
+ ///
+ public void Invoke()
+ {
+ Interlocked.Increment(ref counter);
+ }
+
+ ///
+ /// Throws exception after incrementing counter.
+ ///
+ public void InvokeAndThrowException()
+ {
+ Interlocked.Increment(ref counter);
+ throw new Exception();
+ }
+
+ private void PrivateMethod()
+ {
+ }
+
+ ///
+ /// Returns as return value.
+ ///
+ public string InvokeWithReturnValue()
+ {
+ return DefaultReturnValue;
+ }
+
+ ///
+ /// Invocation count.
+ ///
+ public int CounterValue
+ {
+ get { return counter; }
+ }
+ }
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/QuartzSupportTests.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/QuartzSupportTests.cs
new file mode 100644
index 00000000..882429c9
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/QuartzSupportTests.cs
@@ -0,0 +1,1366 @@
+/*
+ * Copyright 2002-2010 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.Threading;
+
+using NUnit.Framework;
+
+using Quartz;
+using Quartz.Impl;
+using Quartz.Spi;
+
+using Rhino.Mocks;
+
+using Spring.Context.Support;
+using Spring.Objects;
+using Spring.Objects.Factory.Support;
+
+#if QUARTZ_2_0
+using CronTrigger = Quartz.Impl.Triggers.CronTriggerImpl;
+using JobExecutionContext = Quartz.IJobExecutionContext;
+using JobDetail = Quartz.Impl.JobDetailImpl;
+using SimpleTrigger = Quartz.Impl.Triggers.SimpleTriggerImpl;
+using Trigger = Quartz.ITrigger;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ ///
+ /// Juergen Hoeller
+ /// Alef Arendsen
+ /// Rob Harrop
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class QuartzSupportTests
+ {
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ public void TestSchedulerFactoryObject()
+ {
+ DoTestSchedulerFactoryObject(false, false);
+ }
+
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithExplicitJobDetail()
+ {
+ DoTestSchedulerFactoryObject(true, false);
+ }
+
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ [Ignore("Requires change to MethodInvoker for overriding target object and type")]
+ public void TestSchedulerFactoryObjectWithPrototypeJob()
+ {
+ DoTestSchedulerFactoryObject(false, true);
+ }
+
+ private void DoTestSchedulerFactoryObject(bool explicitJobDetail, bool prototypeJob)
+ {
+ TestObject tb = new TestObject("tb", 99);
+ JobDetailObject jobDetail0 = new JobDetailObject();
+ jobDetail0.JobType = typeof (IJob);
+ jobDetail0.ObjectName = ("myJob0");
+ IDictionary jobData = new Hashtable();
+ jobData.Add("testObject", tb);
+ jobDetail0.JobDataAsMap = (jobData);
+ jobDetail0.AfterPropertiesSet();
+ Assert.AreEqual(tb, jobDetail0.JobDataMap.Get("testObject"));
+
+ CronTriggerObject trigger0 = new CronTriggerObject();
+ trigger0.ObjectName = ("myTrigger0");
+ trigger0.JobDetail = (jobDetail0);
+ trigger0.CronExpressionString = ("0/1 * * * * ?");
+ trigger0.AfterPropertiesSet();
+
+ TestMethodInvokingTask task1 = new TestMethodInvokingTask();
+ MethodInvokingJobDetailFactoryObject mijdfb = new MethodInvokingJobDetailFactoryObject();
+ mijdfb.ObjectName = ("myJob1");
+ if (prototypeJob)
+ {
+ StaticListableObjectFactory objectFactory = new StaticListableObjectFactory();
+ objectFactory.AddObject("task", task1);
+ mijdfb.TargetObjectName = ("task");
+ mijdfb.ObjectFactory = objectFactory;
+ }
+ else
+ {
+ mijdfb.TargetObject = (task1);
+ }
+ mijdfb.TargetMethod = ("doSomething");
+ mijdfb.AfterPropertiesSet();
+ JobDetail jobDetail1 = (JobDetail) mijdfb.GetObject();
+
+ SimpleTriggerObject trigger1 = new SimpleTriggerObject();
+ trigger1.ObjectName = ("myTrigger1");
+ trigger1.JobDetail = (jobDetail1);
+ trigger1.StartDelay = TimeSpan.FromMilliseconds(0);
+ trigger1.RepeatInterval = TimeSpan.FromMilliseconds(20);
+ trigger1.AfterPropertiesSet();
+
+ IScheduler scheduler = MockRepository.GenerateMock();
+
+ scheduler.Stub(x => x.Context).Return(new SchedulerContext());
+
+ SchedulerFactoryObject schedulerFactoryObject = new TestSchedulerFactoryObject(scheduler);
+ schedulerFactoryObject.JobFactory = (null);
+ IDictionary schedulerContext = new Hashtable();
+ schedulerContext.Add("otherTestObject", tb);
+ schedulerFactoryObject.SchedulerContextAsMap = (schedulerContext);
+ if (explicitJobDetail)
+ {
+ schedulerFactoryObject.JobDetails = (new JobDetail[] {jobDetail0});
+ }
+ schedulerFactoryObject.Triggers = (new Trigger[] {trigger0, trigger1});
+ try
+ {
+ schedulerFactoryObject.AfterPropertiesSet();
+ schedulerFactoryObject.Start();
+ }
+ finally
+ {
+ schedulerFactoryObject.Dispose();
+ }
+
+ scheduler.AssertWasCalled(x => x.ScheduleJob(trigger0));
+ scheduler.AssertWasCalled(x => x.ScheduleJob(trigger1));
+
+ scheduler.AssertWasCalled(x => x.AddJob(jobDetail0, true));
+ scheduler.AssertWasCalled(x => x.AddJob(jobDetail1, true));
+
+ scheduler.AssertWasCalled(x => x.Start());
+ scheduler.AssertWasCalled(x => x.Shutdown(false));
+ }
+
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithExistingJobs()
+ {
+ DoTestSchedulerFactoryObjectWithExistingJobs(false);
+ }
+
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithOverwriteExistingJobs()
+ {
+ DoTestSchedulerFactoryObjectWithExistingJobs(true);
+ }
+
+ private void DoTestSchedulerFactoryObjectWithExistingJobs(bool overwrite)
+ {
+ TestObject tb = new TestObject("tb", 99);
+ JobDetailObject jobDetail0 = new JobDetailObject();
+ jobDetail0.JobType = typeof (IJob);
+ jobDetail0.ObjectName = ("myJob0");
+ IDictionary jobData = new Hashtable();
+ jobData.Add("testObject", tb);
+ jobDetail0.JobDataAsMap = (jobData);
+ jobDetail0.AfterPropertiesSet();
+ Assert.AreEqual(tb, jobDetail0.JobDataMap.Get("testObject"));
+
+ CronTriggerObject trigger0 = new CronTriggerObject();
+ trigger0.ObjectName = ("myTrigger0");
+ trigger0.JobDetail = (jobDetail0);
+ trigger0.CronExpressionString = ("0/1 * * * * ?");
+ trigger0.AfterPropertiesSet();
+
+ TestMethodInvokingTask task1 = new TestMethodInvokingTask();
+ MethodInvokingJobDetailFactoryObject mijdfb = new MethodInvokingJobDetailFactoryObject();
+ mijdfb.ObjectName = ("myJob1");
+ mijdfb.TargetObject = (task1);
+ mijdfb.TargetMethod = ("doSomething");
+ mijdfb.AfterPropertiesSet();
+ JobDetail jobDetail1 = (JobDetail) mijdfb.GetObject();
+
+ SimpleTriggerObject trigger1 = new SimpleTriggerObject();
+ trigger1.ObjectName = ("myTrigger1");
+ trigger1.JobDetail = (jobDetail1);
+ trigger1.StartDelay = TimeSpan.FromMilliseconds(0);
+ trigger1.RepeatInterval = TimeSpan.FromMilliseconds(20);
+ trigger1.AfterPropertiesSet();
+
+ IScheduler scheduler = MockRepository.GenerateMock();
+ scheduler.Stub(x => x.Context).Return(new SchedulerContext());
+#if QUARTZ_2_0
+ scheduler.Stub(x => x.GetTrigger(new TriggerKey("myTrigger1", SchedulerConstants.DefaultGroup))).Return(new SimpleTrigger());
+#else
+ scheduler.Stub(x => x.GetTrigger("myTrigger1", SchedulerConstants.DefaultGroup)).Return(new SimpleTrigger());
+#endif
+ if (overwrite)
+ {
+#if QUARTZ_2_0
+ scheduler.Stub(x => x.RescheduleJob(new TriggerKey("myTrigger1", SchedulerConstants.DefaultGroup), trigger1)).Return(DateTime.UtcNow);
+#else
+ scheduler.Stub(x => x.RescheduleJob("myTrigger1", SchedulerConstants.DefaultGroup, trigger1)).Return(DateTime.UtcNow);
+#endif
+ }
+
+ SchedulerFactoryObject schedulerFactoryObject = new TestSchedulerFactoryObject(scheduler);
+ schedulerFactoryObject.JobFactory = (null);
+ IDictionary schedulerContext = new Hashtable();
+ schedulerContext.Add("otherTestObject", tb);
+ schedulerFactoryObject.SchedulerContextAsMap = (schedulerContext);
+ schedulerFactoryObject.Triggers = (new Trigger[] {trigger0, trigger1});
+ if (overwrite)
+ {
+ schedulerFactoryObject.OverwriteExistingJobs = (true);
+ }
+ try
+ {
+ schedulerFactoryObject.AfterPropertiesSet();
+ schedulerFactoryObject.Start();
+ }
+ finally
+ {
+ schedulerFactoryObject.Dispose();
+ }
+
+ scheduler.AssertWasCalled(x => x.AddJob(jobDetail0, true));
+ scheduler.AssertWasCalled(x => x.ScheduleJob(trigger0));
+ scheduler.AssertWasCalled(x => x.Start());
+ scheduler.AssertWasCalled(x => x.Shutdown(false));
+ }
+
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithExistingJobsAndRaceCondition()
+ {
+ DoTestSchedulerFactoryObjectWithExistingJobsAndRaceCondition(false);
+ }
+
+ ///
+ /// Executes parametrized test.
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithOverwriteExistingJobsAndRaceCondition()
+ {
+ DoTestSchedulerFactoryObjectWithExistingJobsAndRaceCondition(true);
+ }
+
+ private void DoTestSchedulerFactoryObjectWithExistingJobsAndRaceCondition(bool overwrite)
+ {
+ TestObject tb = new TestObject("tb", 99);
+ JobDetailObject jobDetail0 = new JobDetailObject();
+ jobDetail0.JobType = typeof (IJob);
+ jobDetail0.ObjectName = ("myJob0");
+ IDictionary jobData = new Hashtable();
+ jobData.Add("testObject", tb);
+ jobDetail0.JobDataAsMap = (jobData);
+ jobDetail0.AfterPropertiesSet();
+ Assert.AreEqual(tb, jobDetail0.JobDataMap.Get("testObject"));
+
+ CronTriggerObject trigger0 = new CronTriggerObject();
+ trigger0.ObjectName = ("myTrigger0");
+ trigger0.JobDetail = (jobDetail0);
+ trigger0.CronExpressionString = ("0/1 * * * * ?");
+ trigger0.AfterPropertiesSet();
+
+ TestMethodInvokingTask task1 = new TestMethodInvokingTask();
+ MethodInvokingJobDetailFactoryObject mijdfb = new MethodInvokingJobDetailFactoryObject();
+ mijdfb.ObjectName = ("myJob1");
+ mijdfb.TargetObject = (task1);
+ mijdfb.TargetMethod = ("doSomething");
+ mijdfb.AfterPropertiesSet();
+ JobDetail jobDetail1 = (JobDetail) mijdfb.GetObject();
+
+ SimpleTriggerObject trigger1 = new SimpleTriggerObject();
+ trigger1.ObjectName = ("myTrigger1");
+ trigger1.JobDetail = (jobDetail1);
+ trigger1.StartDelay = TimeSpan.FromMilliseconds(0);
+ trigger1.RepeatInterval = TimeSpan.FromMilliseconds(20);
+ trigger1.AfterPropertiesSet();
+
+ IScheduler scheduler = MockRepository.GenerateMock();
+ scheduler.Stub(x => x.Context).Return(new SchedulerContext());
+#if QUARTZ_2_0
+ scheduler.Stub(x => x.GetTrigger(new TriggerKey("myTrigger1", SchedulerConstants.DefaultGroup))).Return(new SimpleTrigger());
+#else
+ scheduler.Stub(x => x.GetTrigger("myTrigger1", SchedulerConstants.DefaultGroup)).Return(new SimpleTrigger());
+#endif
+ if (overwrite)
+ {
+ scheduler.AddJob(jobDetail1, true);
+#if QUARTZ_2_0
+ scheduler.Stub(x => x.RescheduleJob(new TriggerKey("myTrigger1", SchedulerConstants.DefaultGroup), trigger1)).Return(DateTime.UtcNow);
+#else
+ scheduler.Stub(x => x.RescheduleJob("myTrigger1", SchedulerConstants.DefaultGroup, trigger1)).Return(DateTime.UtcNow);
+#endif
+ }
+
+ scheduler.Stub(x => x.ScheduleJob(trigger0)).Throw(new ObjectAlreadyExistsException(""));
+
+ if (overwrite)
+ {
+#if QUARTZ_2_0
+ scheduler.Stub(x => x.RescheduleJob(new TriggerKey("myTrigger0", SchedulerConstants.DefaultGroup), trigger0)).Return(DateTime.UtcNow);
+#else
+ scheduler.Stub(x => x.RescheduleJob("myTrigger0", SchedulerConstants.DefaultGroup, trigger0)).Return(DateTime.UtcNow);
+#endif
+ }
+
+ scheduler.Start();
+ scheduler.Shutdown(false);
+
+ SchedulerFactoryObject schedulerFactoryObject = new TestSchedulerFactoryObject(scheduler);
+
+ schedulerFactoryObject.JobFactory = (null);
+ IDictionary schedulerContext = new Hashtable();
+ schedulerContext.Add("otherTestObject", tb);
+ schedulerFactoryObject.SchedulerContextAsMap = (schedulerContext);
+ schedulerFactoryObject.Triggers = (new Trigger[] {trigger0, trigger1});
+ if (overwrite)
+ {
+ schedulerFactoryObject.OverwriteExistingJobs = (true);
+ }
+ try
+ {
+ schedulerFactoryObject.AfterPropertiesSet();
+ schedulerFactoryObject.Start();
+ }
+ finally
+ {
+ schedulerFactoryObject.Dispose();
+ }
+
+ scheduler.AssertWasCalled(x => x.AddJob(jobDetail0, true));
+
+ }
+
+#if !QUARTZ_2_0
+ ///
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithListeners()
+ {
+ IJobFactory jobFactory = new AdaptableJobFactory();
+
+ IScheduler scheduler = MockRepository.GenerateMock();
+
+ ISchedulerListener schedulerListener = new TestSchedulerListener();
+ IJobListener globalJobListener = new TestJobListener();
+ IJobListener jobListener = new TestJobListener();
+ ITriggerListener globalTriggerListener = new TestTriggerListener();
+ ITriggerListener triggerListener = new TestTriggerListener();
+
+ Expect.Call(scheduler.JobFactory = (jobFactory));
+ scheduler.AddSchedulerListener(schedulerListener);
+ scheduler.AddGlobalJobListener(globalJobListener);
+ scheduler.AddJobListener(jobListener);
+ scheduler.AddGlobalTriggerListener(globalTriggerListener);
+ scheduler.AddTriggerListener(triggerListener);
+ scheduler.Start();
+ scheduler.Shutdown(false);
+
+ SchedulerFactoryObject schedulerFactoryObject = new TestSchedulerFactoryObject(scheduler);
+
+ schedulerFactoryObject.JobFactory = (jobFactory);
+ schedulerFactoryObject.SchedulerListeners = (new ISchedulerListener[] {schedulerListener});
+ schedulerFactoryObject.GlobalJobListeners = (new IJobListener[] {globalJobListener});
+ schedulerFactoryObject.JobListeners = (new IJobListener[] {jobListener});
+ schedulerFactoryObject.GlobalTriggerListeners = (new ITriggerListener[] {globalTriggerListener});
+ schedulerFactoryObject.TriggerListeners = (new ITriggerListener[] {triggerListener});
+ try
+ {
+ schedulerFactoryObject.AfterPropertiesSet();
+ schedulerFactoryObject.Start();
+ }
+ finally
+ {
+ schedulerFactoryObject.Dispose();
+ }
+ }
+
+#endif
+ /*public void TestMethodInvocationWithConcurrency() {
+ methodInvokingConcurrency(true);
+ }*/
+
+ // We can't test both since Quartz somehow seems to keep things in memory
+ // enable both and one of them will fail (order doesn't matter).
+ /*public void TestMethodInvocationWithoutConcurrency() {
+ methodInvokingConcurrency(false);
+ }*/
+
+ private void methodInvokingConcurrency(bool concurrent)
+ {
+ // Test the concurrency flag.
+ // Method invoking job with two triggers.
+ // If the concurrent flag is false, the triggers are NOT allowed
+ // to interfere with each other.
+
+ TestMethodInvokingTask task1 = new TestMethodInvokingTask();
+ MethodInvokingJobDetailFactoryObject mijdfb = new MethodInvokingJobDetailFactoryObject();
+ // set the concurrency flag!
+ mijdfb.Concurrent = (concurrent);
+ mijdfb.ObjectName = ("myJob1");
+ mijdfb.TargetObject = (task1);
+ mijdfb.TargetMethod = ("doWait");
+ mijdfb.AfterPropertiesSet();
+ JobDetail jobDetail1 = (JobDetail) mijdfb.GetObject();
+
+ SimpleTriggerObject trigger0 = new SimpleTriggerObject();
+ trigger0.ObjectName = ("myTrigger1");
+ trigger0.JobDetail = (jobDetail1);
+ trigger0.StartDelay = TimeSpan.FromMilliseconds(0);
+ trigger0.RepeatInterval = TimeSpan.FromMilliseconds(1);
+ trigger0.RepeatCount = (1);
+ trigger0.AfterPropertiesSet();
+
+ SimpleTriggerObject trigger1 = new SimpleTriggerObject();
+ trigger1.ObjectName = ("myTrigger1");
+ trigger1.JobDetail = (jobDetail1);
+ trigger1.StartDelay = TimeSpan.FromMilliseconds(1000L);
+ trigger1.RepeatInterval = TimeSpan.FromMilliseconds(1);
+ trigger1.RepeatCount = (1);
+ trigger1.AfterPropertiesSet();
+
+ SchedulerFactoryObject schedulerFactoryObject = new SchedulerFactoryObject();
+ schedulerFactoryObject.JobDetails = (new JobDetail[] {jobDetail1});
+ schedulerFactoryObject.Triggers = (new Trigger[] {trigger1, trigger0});
+ schedulerFactoryObject.AfterPropertiesSet();
+
+ // ok scheduler is set up... let's wait for like 4 seconds
+ try
+ {
+ Thread.Sleep(4000);
+ }
+ catch (ThreadInterruptedException)
+ {
+ // fall through
+ }
+
+ if (concurrent)
+ {
+ Assert.AreEqual(2, task1.counter);
+ task1.Stop();
+ // we're done, both jobs have ran, let's call it a day
+ return;
+ }
+ else
+ {
+ Assert.AreEqual(1, task1.counter);
+ task1.Stop();
+ // we need to check whether or not the test succeed with non-concurrent jobs
+ }
+
+ try
+ {
+ Thread.Sleep(4000);
+ }
+ catch (ThreadInterruptedException)
+ {
+ // fall through
+ }
+
+ task1.Stop();
+ Assert.AreEqual(2, task1.counter);
+
+ // Although we're destroying the scheduler, it does seem to keep things in memory:
+ // When executing both tests (concurrent and non-concurrent), the second test always
+ // fails.
+ schedulerFactoryObject.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithPlainQuartzObjects()
+ {
+ IJobFactory jobFactory = new AdaptableJobFactory();
+
+ TestObject tb = new TestObject("tb", 99);
+ JobDetail jobDetail0 = new JobDetail();
+ jobDetail0.JobType = typeof (IJob);
+ jobDetail0.Name = ("myJob0");
+ jobDetail0.Group = (SchedulerConstants.DefaultGroup);
+ jobDetail0.JobDataMap.Add("testObject", tb);
+ Assert.AreEqual(tb, jobDetail0.JobDataMap.Get("testObject"));
+
+ CronTrigger trigger0 = new CronTrigger();
+ trigger0.Name = ("myTrigger0");
+ trigger0.Group = SchedulerConstants.DefaultGroup;
+ trigger0.JobName = "myJob0";
+ trigger0.JobGroup = SchedulerConstants.DefaultGroup;
+ trigger0.StartTimeUtc = (DateTime.UtcNow);
+ trigger0.CronExpressionString = ("0/1 * * * * ?");
+
+ TestMethodInvokingTask task1 = new TestMethodInvokingTask();
+ MethodInvokingJobDetailFactoryObject mijdfb = new MethodInvokingJobDetailFactoryObject();
+ mijdfb.Name = ("myJob1");
+ mijdfb.Group = (SchedulerConstants.DefaultGroup);
+ mijdfb.TargetObject = (task1);
+ mijdfb.TargetMethod = ("doSomething");
+ mijdfb.AfterPropertiesSet();
+ JobDetail jobDetail1 = (JobDetail) mijdfb.GetObject();
+
+ SimpleTrigger trigger1 = new SimpleTrigger();
+ trigger1.Name = "myTrigger1";
+ trigger1.Group = SchedulerConstants.DefaultGroup;
+ trigger1.JobName = "myJob1";
+ trigger1.JobGroup = SchedulerConstants.DefaultGroup;
+ trigger1.StartTimeUtc = (DateTime.UtcNow);
+ trigger1.RepeatCount = (SimpleTrigger.RepeatIndefinitely);
+ trigger1.RepeatInterval = TimeSpan.FromMilliseconds(20);
+
+ IScheduler scheduler = MockRepository.GenerateMock();
+
+ SchedulerFactoryObject schedulerFactoryObject = new TestSchedulerFactoryObject(scheduler);
+
+ schedulerFactoryObject.JobFactory = (jobFactory);
+ schedulerFactoryObject.JobDetails = (new JobDetail[] {jobDetail0, jobDetail1});
+ schedulerFactoryObject.Triggers = (new Trigger[] {trigger0, trigger1});
+ try
+ {
+ schedulerFactoryObject.AfterPropertiesSet();
+ schedulerFactoryObject.Start();
+ }
+ finally
+ {
+ schedulerFactoryObject.Dispose();
+ }
+
+ scheduler.AssertWasCalled(x => x.JobFactory = jobFactory);
+ scheduler.AssertWasCalled(x => x.AddJob(jobDetail0, true));
+ scheduler.AssertWasCalled(x => x.AddJob(jobDetail1, true));
+#if QUARTZ_2_0
+ scheduler.AssertWasCalled(x => x.GetJobDetail(new JobKey("myJob0", SchedulerConstants.DefaultGroup)));
+ scheduler.AssertWasCalled(x => x.GetJobDetail(new JobKey("myJob1", SchedulerConstants.DefaultGroup)));
+ scheduler.AssertWasCalled(x => x.GetTrigger(new TriggerKey("myTrigger0", SchedulerConstants.DefaultGroup)));
+ scheduler.AssertWasCalled(x => x.GetTrigger(new TriggerKey("myTrigger1", SchedulerConstants.DefaultGroup)));
+#else
+ scheduler.AssertWasCalled(x => x.GetJobDetail("myJob0", SchedulerConstants.DefaultGroup));
+ scheduler.AssertWasCalled(x => x.GetJobDetail("myJob1", SchedulerConstants.DefaultGroup));
+ scheduler.AssertWasCalled(x => x.GetTrigger("myTrigger0", SchedulerConstants.DefaultGroup));
+ scheduler.AssertWasCalled(x => x.GetTrigger("myTrigger1", SchedulerConstants.DefaultGroup));
+#endif
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerFactoryObjectWithApplicationContext()
+ {
+ TestObject tb = new TestObject("tb", 99);
+ StaticApplicationContext ac = new StaticApplicationContext();
+
+ IScheduler scheduler = MockRepository.GenerateMock();
+ SchedulerContext schedulerContext = new SchedulerContext();
+ scheduler.Stub(x => x.Context).Return(schedulerContext).Repeat.Times(4);
+
+ SchedulerFactoryObject schedulerFactoryObject = new TestSchedulerFactoryObject(scheduler);
+ schedulerFactoryObject.JobFactory = (null);
+ IDictionary schedulerContextMap = new Hashtable();
+ schedulerContextMap.Add("testObject", tb);
+ schedulerFactoryObject.SchedulerContextAsMap = (schedulerContextMap);
+ schedulerFactoryObject.ApplicationContext = (ac);
+ schedulerFactoryObject.ApplicationContextSchedulerContextKey = ("appCtx");
+ try
+ {
+ schedulerFactoryObject.AfterPropertiesSet();
+ schedulerFactoryObject.Start();
+ IScheduler returnedScheduler = (IScheduler) schedulerFactoryObject.GetObject();
+ Assert.AreEqual(tb, returnedScheduler.Context["testObject"]);
+ Assert.AreEqual(ac, returnedScheduler.Context["appCtx"]);
+ }
+ finally
+ {
+ schedulerFactoryObject.Dispose();
+ }
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestJobDetailObjectWithApplicationContext()
+ {
+ TestObject tb = new TestObject("tb", 99);
+ StaticApplicationContext ac = new StaticApplicationContext();
+
+ JobDetailObject jobDetail = new JobDetailObject();
+ jobDetail.JobType = typeof (IJob);
+ jobDetail.ObjectName = ("myJob0");
+ IDictionary jobData = new Hashtable();
+ jobData.Add("testObject", tb);
+ jobDetail.JobDataAsMap = (jobData);
+ jobDetail.ApplicationContext = (ac);
+ jobDetail.ApplicationContextJobDataKey = ("appCtx");
+ jobDetail.AfterPropertiesSet();
+
+ Assert.AreEqual(tb, jobDetail.JobDataMap.Get("testObject"));
+ Assert.AreEqual(ac, jobDetail.JobDataMap.Get("appCtx"));
+ }
+
+#if !QUARTZ_2_0
+ ///
+ ///
+ [Test]
+ public void TestMethodInvokingJobDetailFactoryObjectWithListenerNames()
+ {
+ TestMethodInvokingTask task = new TestMethodInvokingTask();
+ MethodInvokingJobDetailFactoryObject mijdfb = new MethodInvokingJobDetailFactoryObject();
+ String[] names = new String[] {"test1", "test2"};
+ mijdfb.Name = ("myJob1");
+ mijdfb.Group = (SchedulerConstants.DefaultGroup);
+ mijdfb.TargetObject = (task);
+ mijdfb.TargetMethod = ("doSomething");
+ mijdfb.JobListenerNames = (names);
+ mijdfb.AfterPropertiesSet();
+ JobDetail jobDetail = (JobDetail) mijdfb.GetObject();
+ ArrayList result = new ArrayList(jobDetail.JobListenerNames);
+ Assert.AreEqual(names, result);
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestJobDetailObjectWithListenerNames()
+ {
+ JobDetailObject jobDetail = new JobDetailObject();
+ String[] names = new String[] {"test1", "test2"};
+ jobDetail.JobListenerNames = (names);
+ ArrayList result = new ArrayList(jobDetail.JobListenerNames);
+ Assert.AreEqual(names, result);
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestCronTriggerObjectWithListenerNames()
+ {
+ CronTriggerObject trigger = new CronTriggerObject();
+ String[] names = new String[] {"test1", "test2"};
+ trigger.TriggerListenerNames = (names);
+ ArrayList result = new ArrayList(trigger.TriggerListenerNames);
+ Assert.AreEqual(names, result);
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSimpleTriggerObjectWithListenerNames()
+ {
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ String[] names = new String[] {"test1", "test2"};
+ trigger.TriggerListenerNames = (names);
+ ArrayList result = new ArrayList(trigger.TriggerListenerNames);
+ Assert.AreEqual(names, result);
+ }
+#endif
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithTaskExecutor()
+ {
+ CountingTaskExecutor taskExecutor = new CountingTaskExecutor();
+ DummyJob.count = 0;
+
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.JobType = typeof (DummyJob);
+ ;
+ jobDetail.Name = ("myJob");
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = ("myTrigger");
+ trigger.JobDetail = (jobDetail);
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = (1);
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.TaskExecutor = (taskExecutor);
+ factoryObject.Triggers = (new Trigger[] {trigger});
+ factoryObject.JobDetails = (new JobDetail[] {jobDetail});
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ Thread.Sleep(500);
+ Assert.IsTrue(DummyJob.count > 0);
+ Assert.AreEqual(DummyJob.count, taskExecutor.count);
+
+ factoryObject.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithRunnable()
+ {
+ DummyRunnable.count = 0;
+
+ JobDetail jobDetail = new JobDetailObject();
+ jobDetail.JobType = typeof (DummyRunnable);
+ jobDetail.Name = "myJob";
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = "myTrigger";
+ trigger.JobDetail = jobDetail;
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = 1;
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.Triggers = new Trigger[] {trigger};
+ factoryObject.JobDetails = new JobDetail[] {jobDetail};
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ DummyRunnable.runEvent.WaitOne(500);
+ Assert.IsTrue(DummyRunnable.count > 0);
+
+ factoryObject.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithQuartzJobObject()
+ {
+ DummyJob.param = 0;
+ DummyJob.count = 0;
+
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.JobType = (typeof (DummyJobObject));
+ jobDetail.Name = ("myJob");
+ jobDetail.JobDataMap.Put("param", "10");
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = ("myTrigger");
+ trigger.JobDetail = (jobDetail);
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = (1);
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.Triggers = (new Trigger[] {trigger});
+ factoryObject.JobDetails = (new JobDetail[] {jobDetail});
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ Thread.Sleep(500);
+ Assert.AreEqual(10, DummyJobObject.param);
+ Assert.IsTrue(DummyJobObject.count > 0);
+
+ factoryObject.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithSpringObjectJobFactory()
+ {
+ DummyJob.param = 0;
+ DummyJob.count = 0;
+
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.JobType = typeof(DummyJob);
+ jobDetail.Name = ("myJob");
+ jobDetail.JobDataMap.Add("param", "10");
+ jobDetail.JobDataMap.Add("ignoredParam", "10");
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = ("myTrigger");
+ trigger.JobDetail = (jobDetail);
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = (1);
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.JobFactory = (new SpringObjectJobFactory());
+ factoryObject.Triggers = (new Trigger[] {trigger});
+ factoryObject.JobDetails = (new JobDetail[] {jobDetail});
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ Thread.Sleep(500);
+ Assert.AreEqual(10, DummyJob.param);
+ Assert.IsTrue(DummyJob.count > 0);
+
+ factoryObject.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithSpringObjectJobFactoryAndParamMismatchNotIgnored()
+ {
+ DummyJob.param = 0;
+ DummyJob.count = 0;
+
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.JobType = typeof(DummyJob);
+ jobDetail.Name = ("myJob");
+ jobDetail.JobDataMap.Add("para", "10");
+ jobDetail.JobDataMap.Add("ignoredParam", "10");
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = ("myTrigger");
+ trigger.JobDetail = (jobDetail);
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = (1);
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject bean = new SchedulerFactoryObject();
+ SpringObjectJobFactory jobFactory = new SpringObjectJobFactory();
+ jobFactory.IgnoredUnknownProperties = (new String[] {"ignoredParam"});
+ bean.JobFactory = (jobFactory);
+ bean.Triggers = (new Trigger[] {trigger});
+ bean.JobDetails = (new JobDetail[] {jobDetail});
+ bean.AfterPropertiesSet();
+
+ Thread.Sleep(500);
+ Assert.AreEqual(0, DummyJob.param);
+ Assert.IsTrue(DummyJob.count == 0);
+
+ bean.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithSpringObjectJobFactoryAndRunnable()
+ {
+ DummyRunnable.param = 0;
+ DummyRunnable.count = 0;
+
+ JobDetail jobDetail = new JobDetailObject();
+ jobDetail.JobType = typeof (DummyRunnable);
+ jobDetail.Name = ("myJob");
+ jobDetail.JobDataMap.Add("param", "10");
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = ("myTrigger");
+ trigger.JobDetail = (jobDetail);
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = (1);
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.JobFactory = (new SpringObjectJobFactory());
+ factoryObject.Triggers = (new Trigger[] {trigger});
+ factoryObject.JobDetails = (new JobDetail[] {jobDetail});
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ Thread.Sleep(500);
+ Assert.AreEqual(10, DummyRunnable.param);
+ Assert.IsTrue(DummyRunnable.count > 0);
+
+ factoryObject.Dispose();
+ }
+
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithSpringObjectJobFactoryAndQuartzJobObject()
+ {
+ DummyJobObject.param = 0;
+ DummyJobObject.count = 0;
+
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.JobType = typeof (DummyJobObject);
+ jobDetail.Name = ("myJob");
+ jobDetail.JobDataMap.Add("param", "10");
+
+ SimpleTriggerObject trigger = new SimpleTriggerObject();
+ trigger.Name = ("myTrigger");
+ trigger.JobDetail = (jobDetail);
+ trigger.StartDelay = TimeSpan.FromMilliseconds(1);
+ trigger.RepeatInterval = TimeSpan.FromMilliseconds(500);
+ trigger.RepeatCount = (1);
+ trigger.AfterPropertiesSet();
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.JobFactory = (new SpringObjectJobFactory());
+ factoryObject.Triggers = (new Trigger[] {trigger});
+ factoryObject.JobDetails = (new JobDetail[] {jobDetail});
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ Thread.Sleep(500);
+ Assert.AreEqual(10, DummyJobObject.param);
+ Assert.IsTrue(DummyJobObject.count > 0);
+
+ factoryObject.Dispose();
+ }
+
+ ///
+ ///
+ ///
+ [Test]
+ public void TestSchedulerWithSpringObjectJobFactoryAndJobSchedulingData()
+ {
+ DummyJob.param = 0;
+ DummyJob.count = 0;
+
+ SchedulerFactoryObject factoryObject = new SchedulerFactoryObject();
+ factoryObject.JobFactory = new SpringObjectJobFactory();
+ factoryObject.JobSchedulingDataLocation = "job-scheduling-data.xml";
+ // TODO bean.ResourceLoader = (new FileSystemResourceLoader());
+ factoryObject.AfterPropertiesSet();
+ factoryObject.Start();
+
+ Thread.Sleep(500);
+ Assert.AreEqual(10, DummyJob.param);
+ Assert.IsTrue(DummyJob.count > 0);
+
+ factoryObject.Dispose();
+ }
+
+
+ ///
+ /// Tests the creation of multiple schedulers (SPR-772)
+ ///
+ [Test]
+ public void TestMultipleSchedulers()
+ {
+ XmlApplicationContext ctx = new XmlApplicationContext("multipleSchedulers.xml");
+ try
+ {
+ IScheduler scheduler1 = (IScheduler) ctx.GetObject("scheduler1");
+ IScheduler scheduler2 = (IScheduler) ctx.GetObject("scheduler2");
+ Assert.AreNotSame(scheduler1, scheduler2);
+ Assert.AreEqual("quartz1", scheduler1.SchedulerName);
+ Assert.AreEqual("quartz2", scheduler2.SchedulerName);
+
+ XmlApplicationContext ctx2 = new XmlApplicationContext("multipleSchedulers.xml");
+ try
+ {
+ IScheduler scheduler1a = (IScheduler) ctx2.GetObject("scheduler1");
+ IScheduler scheduler2a = (IScheduler) ctx2.GetObject("scheduler2");
+ Assert.AreNotSame(scheduler1a, scheduler2a);
+ Assert.AreNotSame(scheduler1a, scheduler1);
+ Assert.AreNotSame(scheduler2a, scheduler2);
+ Assert.AreEqual("quartz1", scheduler1a.SchedulerName);
+ Assert.AreEqual("quartz2", scheduler2a.SchedulerName);
+ }
+ finally
+ {
+ ctx2.Dispose();
+ }
+ }
+ finally
+ {
+ ctx.Dispose();
+ }
+ }
+
+ ///
+ /// Tests calling of services with method invoke.
+ ///
+ [Test]
+ public void TestWithTwoAnonymousMethodInvokingJobDetailFactoryObjects()
+ {
+ XmlApplicationContext ctx = new XmlApplicationContext("multipleAnonymousMethodInvokingJobDetailFB.xml");
+ Thread.Sleep(3000);
+ try
+ {
+ QuartzTestObject exportService = (QuartzTestObject) ctx.GetObject("exportService");
+ QuartzTestObject importService = (QuartzTestObject) ctx.GetObject("importService");
+
+ Assert.AreEqual(0, exportService.ImportCount, "doImport called exportService");
+ Assert.AreEqual(2, exportService.ExportCount, "doExport not called on exportService");
+ Assert.AreEqual(2, importService.ImportCount, "doImport not called on importService");
+ Assert.AreEqual(0, importService.ExportCount, "doExport called on importService");
+ }
+ finally
+ {
+ ctx.Dispose();
+ }
+ }
+
+ ///
+ /// Tests how quartz triggers and services interact.
+ ///
+ [Test]
+ public void TestSchedulerAccessorObject()
+ {
+ XmlApplicationContext ctx = new XmlApplicationContext("schedulerAccessorObject.xml");
+ Thread.Sleep(3000);
+ try
+ {
+ QuartzTestObject exportService = (QuartzTestObject) ctx.GetObject("exportService");
+ QuartzTestObject importService = (QuartzTestObject) ctx.GetObject("importService");
+
+ Assert.AreEqual(0, exportService.ImportCount, "doImport called exportService");
+ Assert.AreEqual(2, exportService.ExportCount, "doExport not called on exportService");
+ Assert.AreEqual(2, importService.ImportCount, "doImport not called on importService");
+ Assert.AreEqual(0, importService.ExportCount, "doExport called on importService");
+ }
+ finally
+ {
+ ctx.Dispose();
+ }
+ }
+
+ [Test]
+ public void TestSchedulerAutoStartsOnContextRefreshedEventByDefault()
+ {
+ StaticApplicationContext context = new StaticApplicationContext();
+ context.RegisterObjectDefinition("scheduler", new RootObjectDefinition(typeof (SchedulerFactoryObject)));
+ IScheduler scheduler = (IScheduler) context.GetObject("scheduler", typeof (IScheduler));
+ Assert.IsFalse(scheduler.IsStarted);
+ context.Refresh();
+ Assert.IsTrue(scheduler.IsStarted);
+ }
+
+ [Test]
+ public void TestSchedulerAutoStartupFalse()
+ {
+ StaticApplicationContext context = new StaticApplicationContext();
+ ObjectDefinitionBuilder beanDefinition = ObjectDefinitionBuilder
+ .GenericObjectDefinition(typeof(SchedulerFactoryObject))
+ .AddPropertyValue("autoStartup", false);
+
+ context.RegisterObjectDefinition("scheduler", beanDefinition.ObjectDefinition);
+ IScheduler scheduler = (IScheduler) context.GetObject("scheduler", typeof(IScheduler));
+
+ Assert.IsFalse(scheduler.IsStarted);
+ context.Refresh();
+ Assert.IsFalse(scheduler.IsStarted);
+ }
+
+ ///
+ /// Tests how scheduler is exposed to application context.
+ ///
+ [Test]
+ public void TestSchedulerRepositoryExposure()
+ {
+ XmlApplicationContext ctx = new XmlApplicationContext("schedulerRepositoryExposure.xml");
+ Assert.AreSame(SchedulerRepository.Instance.Lookup("myScheduler"), ctx.GetObject("scheduler"));
+ ctx.Dispose();
+ }
+
+
+#if QUARTZ_2_0
+ private class TestSchedulerListener : ISchedulerListener
+ {
+ public void JobScheduled(ITrigger trigger)
+ {
+ }
+
+ public void JobUnscheduled(TriggerKey triggerKey)
+ {
+ }
+
+ public void TriggerFinalized(ITrigger trigger)
+ {
+ }
+
+ public void TriggerPaused(TriggerKey triggerKey)
+ {
+ }
+
+ public void TriggersPaused(string triggerGroup)
+ {
+ }
+
+ public void TriggerResumed(TriggerKey triggerKey)
+ {
+ }
+
+ public void TriggersResumed(string triggerGroup)
+ {
+ }
+
+ public void JobAdded(IJobDetail jobDetail)
+ {
+ }
+
+ public void JobDeleted(JobKey jobKey)
+ {
+ }
+
+ public void JobPaused(JobKey jobKey)
+ {
+ }
+
+ public void JobsPaused(string jobGroup)
+ {
+ }
+
+ public void JobResumed(JobKey jobKey)
+ {
+ }
+
+ public void JobsResumed(string jobGroup)
+ {
+ }
+
+ public void SchedulerError(string msg, SchedulerException cause)
+ {
+ }
+
+ public void SchedulerInStandbyMode()
+ {
+ }
+
+ public void SchedulerStarted()
+ {
+ }
+
+ public void SchedulerShutdown()
+ {
+ }
+
+ public void SchedulerShuttingdown()
+ {
+ }
+
+ public void SchedulingDataCleared()
+ {
+ }
+ }
+
+#else
+ private class TestSchedulerListener : ISchedulerListener
+ {
+ public void JobScheduled(Trigger trigger)
+ {
+ }
+
+ public void JobUnscheduled(String triggerName, String triggerGroup)
+ {
+ }
+
+ public void TriggerFinalized(Trigger trigger)
+ {
+ }
+
+ public void TriggersPaused(String triggerName, String triggerGroup)
+ {
+ }
+
+ public void TriggersResumed(String triggerName, String triggerGroup)
+ {
+ }
+
+ public void JobsPaused(String jobName, String jobGroup)
+ {
+ }
+
+ public void JobsResumed(String jobName, String jobGroup)
+ {
+ }
+
+ public void SchedulerError(String msg, SchedulerException cause)
+ {
+ }
+
+ public void SchedulerShutdown()
+ {
+ }
+ }
+#endif
+
+ private class TestJobListener : IJobListener
+ {
+ public string Name
+ {
+ get { return null; }
+ }
+
+ public void JobToBeExecuted(JobExecutionContext context)
+ {
+ }
+
+ public void JobExecutionVetoed(JobExecutionContext context)
+ {
+ }
+
+ public void JobWasExecuted(JobExecutionContext context, JobExecutionException jobException)
+ {
+ }
+ }
+
+
+ private class TestTriggerListener : ITriggerListener
+ {
+ public string Name
+ {
+ get { return null; }
+ }
+
+ public void TriggerFired(Trigger trigger, JobExecutionContext context)
+ {
+ }
+
+ public bool VetoJobExecution(Trigger trigger, JobExecutionContext context)
+ {
+ return false;
+ }
+
+ public void TriggerMisfired(Trigger trigger)
+ {
+ }
+
+ public void TriggerComplete(Trigger trigger, JobExecutionContext context,
+ SchedulerInstruction triggerInstructionCode)
+ {
+ }
+ }
+
+
+ ///
+ /// Simple task executor that tracks invocation count.
+ ///
+ public class CountingTaskExecutor : ITaskExecutor
+ {
+ internal int count;
+
+ ///
+ /// Executes task instance.
+ ///
+ ///
+ public void Execute(ThreadStart task)
+ {
+ count++;
+ task.Invoke();
+ }
+ }
+
+ ///
+ /// Simple test job object.
+ ///
+ public class DummyJobObject : QuartzJobObject
+ {
+ internal static int param;
+ internal static int count;
+
+ ///
+ /// Sets parameter value.
+ ///
+ ///
+ public void SetParam(int value)
+ {
+ if (param > 0)
+ {
+ throw new NotSupportedException("Param already set");
+ }
+ param = value;
+ }
+
+ ///
+ /// Execute the actual job. The job data map will already have been
+ /// applied as object property values by execute. The contract is
+ /// exactly the same as for the standard Quartz execute method.
+ ///
+ protected override void ExecuteInternal(JobExecutionContext jobExecutionContext)
+ {
+ count++;
+ }
+ }
+
+ ///
+ /// Simple thread runnable.
+ ///
+ public class DummyRunnable : IThreadRunnable
+ {
+ internal static int param;
+ internal static int count;
+ internal static readonly ManualResetEvent runEvent = new ManualResetEvent(false);
+
+ ///
+ /// Runs thread runnable.
+ ///
+ public void Run()
+ {
+ count++;
+ runEvent.Set();
+ }
+ }
+ }
+
+ ///
+ /// A simple job that tracks invocation count and allows setting of a simple parameter.
+ ///
+ public class DummyJob : IJob
+ {
+ internal static int param;
+ internal static int count;
+
+ ///
+ /// Sets param value.
+ ///
+ ///
+ public void SetParam(int value)
+ {
+ if (param > 0)
+ {
+ throw new NotSupportedException("Param already set");
+ }
+ param = value;
+ }
+
+ ///
+ /// Executes this job instance.
+ ///
+ ///
+ public void Execute(JobExecutionContext jobExecutionContext)
+ {
+ count++;
+ }
+ }
+
+ ///
+ /// Subclass of SchedulerFactoryObject for testing purposes.
+ ///
+ public class TestSchedulerFactoryObject : SchedulerFactoryObject
+ {
+ private readonly IScheduler sched;
+
+ ///
+ /// Creates new instance of this class.
+ ///
+ ///
+ public TestSchedulerFactoryObject(IScheduler sched)
+ {
+ this.sched = sched;
+ }
+
+ ///
+ /// Creates a scheduler actually returning the scheduler this intance
+ /// was instantiated with.
+ ///
+ ///
+ ///
+ ///
+ protected override IScheduler CreateScheduler(ISchedulerFactory schedulerFactory, String schedulerName)
+ {
+ return sched;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/QuartzTestObject.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/QuartzTestObject.cs
new file mode 100644
index 00000000..2d1e1346
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/QuartzTestObject.cs
@@ -0,0 +1,45 @@
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// A simple test object for Quartz.NET to run
+ /// that simulates imports and exports.
+ ///
+ /// Rob Harrop
+ public class QuartzTestObject
+ {
+ private int exportCount;
+ private int importCount;
+
+ ///
+ /// Executes a fake import and increments counter.
+ ///
+ public void DoImport()
+ {
+ ++importCount;
+ }
+
+ ///
+ /// Executes a fake export and increments counter.
+ ///
+ public void DoExport()
+ {
+ ++exportCount;
+ }
+
+ ///
+ /// Tells how many times import has been done.
+ ///
+ public int ImportCount
+ {
+ get { return importCount; }
+ }
+
+ ///
+ /// Tells how many times export has been done.
+ ///
+ public int ExportCount
+ {
+ get { return exportCount; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SchedulerFactoryObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SchedulerFactoryObjectTest.cs
new file mode 100644
index 00000000..7f3bc716
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SchedulerFactoryObjectTest.cs
@@ -0,0 +1,386 @@
+/*
+* Copyright 2002-2010 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.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using NUnit.Framework;
+
+using Quartz;
+using Quartz.Impl;
+using Quartz.Spi;
+using Rhino.Mocks;
+
+using Spring.Core.IO;
+
+#if QUARTZ_2_0
+using Trigger = Quartz.ITrigger;
+using JobExecutionContext = Quartz.IJobExecutionContext;
+using JobDetail = Quartz.IJobDetail;
+using SimpleTrigger = Quartz.Impl.Triggers.SimpleTriggerImpl;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Tests for SchedulerFactoryObject.
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class SchedulerFactoryObjectTest
+ {
+ private static readonly MethodInfo m_InitSchedulerFactory = typeof(SchedulerFactoryObject).GetMethod("InitSchedulerFactory",
+ BindingFlags.Instance | BindingFlags.NonPublic);
+ private SchedulerFactoryObject factory;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ factory = new SchedulerFactoryObject();
+
+ TestSchedulerFactory.Initialize();
+ TestSchedulerFactory.MockScheduler.Stub(x => x.SchedulerName).Return("scheduler").Repeat.Any();
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_Defaults()
+ {
+ factory.AfterPropertiesSet();
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_NullJobFactory()
+ {
+ factory.JobFactory = null;
+ factory.AfterPropertiesSet();
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_NoAutoStartup()
+ {
+ // set expectations
+ TestSchedulerFactory.MockScheduler.JobFactory = null;
+
+ factory.SchedulerFactoryType = typeof(TestSchedulerFactory);
+ factory.AutoStartup = false;
+ factory.AfterPropertiesSet();
+
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.JobFactory = null);
+ }
+
+#if !QUARTZ_2_0
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_AddListeners()
+ {
+ InitForAfterPropertiesSetTest();
+
+ factory.SchedulerListeners = new ISchedulerListener[] { MockRepository.GenerateMock() };
+
+ factory.GlobalJobListeners = new IJobListener[] { MockRepository.GenerateMock() };
+
+ factory.JobListeners = new IJobListener[] { MockRepository.GenerateMock() };
+
+ factory.GlobalTriggerListeners = new ITriggerListener[] { MockRepository.GenerateMock() };
+
+ factory.TriggerListeners = new ITriggerListener[] { MockRepository.GenerateMock() };
+
+ factory.AfterPropertiesSet();
+
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddSchedulerListener(Arg.Is.NotNull));
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddGlobalJobListener(Arg.Is.NotNull));
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddJobListener(Arg.Is.NotNull));
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddGlobalTriggerListener(Arg.Is.NotNull));
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddTriggerListener(Arg.Is.NotNull));
+ }
+#endif
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_Calendars()
+ {
+ InitForAfterPropertiesSetTest();
+
+ const string calendarName = "calendar";
+ ICalendar cal = MockRepository.GenerateMock();
+ Hashtable calTable = new Hashtable();
+ calTable[calendarName] = cal;
+ factory.Calendars = calTable;
+
+ factory.AfterPropertiesSet();
+
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddCalendar(calendarName, cal, true, true));
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_Trigger_TriggerExists()
+ {
+ InitForAfterPropertiesSetTest();
+
+ const string TRIGGER_NAME = "trigName";
+ const string TRIGGER_GROUP = "trigGroup";
+ SimpleTrigger trigger = new SimpleTrigger(TRIGGER_NAME, TRIGGER_GROUP);
+ factory.Triggers = new Trigger[] { trigger };
+
+#if QUARTZ_2_0
+ TestSchedulerFactory.MockScheduler.Stub(x => x.GetTrigger(new TriggerKey(TRIGGER_NAME, TRIGGER_GROUP))).Return(trigger);
+#else
+ TestSchedulerFactory.MockScheduler.Stub(x => x.GetTrigger(TRIGGER_NAME, TRIGGER_GROUP)).Return(trigger);
+#endif
+
+ factory.AfterPropertiesSet();
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_Trigger_TriggerDoesntExist()
+ {
+ InitForAfterPropertiesSetTest();
+
+ const string TRIGGER_NAME = "trigName";
+ const string TRIGGER_GROUP = "trigGroup";
+ SimpleTrigger trigger = new SimpleTrigger(TRIGGER_NAME, TRIGGER_GROUP);
+ factory.Triggers = new Trigger[] { trigger };
+
+ factory.AfterPropertiesSet();
+
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.ScheduleJob(trigger));
+ }
+
+
+ private void InitForAfterPropertiesSetTest()
+ {
+ factory.AutoStartup = false;
+ // set expectations
+ factory.SchedulerFactoryType = typeof(TestSchedulerFactory);
+ TestSchedulerFactory.MockScheduler.JobFactory = null;
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestStart()
+ {
+ factory.SchedulerFactoryType = typeof(TestSchedulerFactory);
+ factory.AutoStartup = false;
+ factory.AfterPropertiesSet();
+ factory.Start();
+
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.JobFactory = Arg.Is.NotNull);
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.Start());
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestStop()
+ {
+ factory.SchedulerFactoryType = typeof(TestSchedulerFactory);
+ factory.AutoStartup = false;
+ factory.AfterPropertiesSet();
+ factory.Stop();
+
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.JobFactory = Arg.Is.NotNull);
+ TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.Standby());
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestGetObject()
+ {
+ factory.AfterPropertiesSet();
+ IScheduler sched = (IScheduler)factory.GetObject();
+ Assert.IsNotNull(sched, "scheduler was null");
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void TestSchedulerFactoryType_InvalidType()
+ {
+ factory.SchedulerFactoryType = typeof(SchedulerFactoryObjectTest);
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestSchedulerFactoryType_ValidType()
+ {
+ factory.SchedulerFactoryType = typeof(StdSchedulerFactory);
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestInitSchedulerFactory_MinimalDefaults()
+ {
+ factory.SchedulerName = "testFactoryObject";
+ StdSchedulerFactory factoryToPass = new StdSchedulerFactory();
+ m_InitSchedulerFactory.Invoke(factory, new object[] { factoryToPass });
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestInitSchedulerFactory_ConfigLocationReadingShouldPreserverExtraEqualsMarksAndTrimKeysAndValues()
+ {
+ const string ConnectionStringValue = "Server=(local);Database=quartz;Trusted_Connection=True;";
+ const string ConnectionStringKey = "quartz.dataSource.default.connectionString";
+ string configuration =
+ @"quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
+quartz.jobStore.useProperties = false
+quartz.jobStore.dataSource = default" + Environment.NewLine +
+ConnectionStringKey+ " = " + ConnectionStringValue + Environment.NewLine +
+"quartz.dataSource.default.provider = SqlServer-20";
+
+ // initialize data
+ MemoryStream ms = new MemoryStream();
+ byte[] data = Encoding.UTF8.GetBytes(configuration);
+ ms.Write(data, 0, data.Length);
+ ms.Seek(0, SeekOrigin.Begin);
+ ms.Position = 0;
+
+ // intercept call
+ InterceptingStdSChedulerFactory factoryToPass = new InterceptingStdSChedulerFactory();
+
+ factory.ConfigLocation = new TestConfigLocation(ms, "description");
+
+ m_InitSchedulerFactory.Invoke(factory, new object[] { factoryToPass });
+
+ Assert.AreEqual(ConnectionStringValue, factoryToPass.Properties[ConnectionStringKey]);
+
+ }
+ }
+
+ internal class TestConfigLocation : InputStreamResource
+ {
+ public TestConfigLocation(Stream inputStream, string description) : base(inputStream, description)
+ {
+ }
+ }
+
+ ///
+ /// ISchedulerFactory implementation for testing purposes.
+ ///
+ public class TestSchedulerFactory : ISchedulerFactory
+ {
+ private static IScheduler mockScheduler;
+
+ ///
+ /// The mocked scheduler.
+ ///
+ public static IScheduler MockScheduler
+ {
+ get { return mockScheduler; }
+ }
+
+ ///
+ ///
+ ///
+ public IScheduler GetScheduler()
+ {
+ return mockScheduler;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public IScheduler GetScheduler(string schedName)
+ {
+ return mockScheduler;
+ }
+
+#if QUARTZ_2_0
+ ///
+ ///
+ public ICollection AllSchedulers
+ {
+ get { return new List(); }
+ }
+#else
+ ///
+ ///
+ public ICollection AllSchedulers
+ {
+ get { return new ArrayList(); }
+ }
+#endif
+
+ public static void Initialize()
+ {
+ mockScheduler = MockRepository.GenerateMock();
+ }
+ }
+
+ ///
+ /// Scheduler factory that supports property interception.
+ ///
+ public class InterceptingStdSChedulerFactory : StdSchedulerFactory
+ {
+ private NameValueCollection properties;
+
+ ///
+ /// Initializes the factory.
+ ///
+ ///
+ public override void Initialize(NameValueCollection props)
+ {
+ this.properties = props;
+ }
+
+ ///
+ /// Return propeties given to this factory at initialization time.
+ ///
+ public NameValueCollection Properties
+ {
+ get { return properties; }
+ }
+ }
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SimpleTriggerObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SimpleTriggerObjectTest.cs
new file mode 100644
index 00000000..3486f2ad
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SimpleTriggerObjectTest.cs
@@ -0,0 +1,134 @@
+/*
+* Copyright 2002-2010 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.Generic;
+using NUnit.Framework;
+
+using Quartz;
+using Quartz.Job;
+
+#if QUARTZ_2_0
+using JobDetail = Quartz.Impl.JobDetailImpl;
+#endif
+
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Tests for .
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class SimpleTriggerObjectTest : TriggerObjectTest
+ {
+ private SimpleTriggerObject simpleTrigger;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ simpleTrigger = new SimpleTriggerObject();
+ simpleTrigger.ObjectName = TRIGGER_NAME;
+ Trigger = simpleTrigger;
+ }
+
+ ///
+ /// Tests all possible misfire instructions for cron trigger
+ /// from strings to int.
+ ///
+ [Test]
+ public void TestMisfireInstructionNames()
+ {
+ string[] names = new string[] { "FireNow", "RescheduleNextWithExistingCount", "RescheduleNextWithRemainingCount", "RescheduleNowWithExistingRepeatCount", "RescheduleNowWithRemainingRepeatCount", "SmartPolicy" };
+ foreach (string name in names)
+ {
+ simpleTrigger.MisfireInstructionName = name;
+ }
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public override void TestAfterPropertiesSet_Defaults()
+ {
+ simpleTrigger.AfterPropertiesSet();
+ base.TestAfterPropertiesSet_Defaults();
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public override void TestAfterPropertiesSet_ValuesGiven()
+ {
+ simpleTrigger.StartDelay = TimeSpan.FromMilliseconds(100);
+ simpleTrigger.AfterPropertiesSet();
+ base.TestAfterPropertiesSet_ValuesGiven();
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestAfterPropertiesSet_StartDelayGiven()
+ {
+ const int START_DELAY = 100000;
+ simpleTrigger.StartDelay = TimeSpan.FromMilliseconds(START_DELAY);
+ DateTime startTime = DateTime.UtcNow;
+ simpleTrigger.AfterPropertiesSet();
+ AssertDateTimesEqualityWithAllowedDelta(startTime.AddMilliseconds(START_DELAY), simpleTrigger.StartTimeUtc, 1000);
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public override void TestAfterPropertiesSet_JobDetailGiven()
+ {
+ const string jobName = "jobName";
+ const string jobGroup = "jobGroup";
+ JobDetail jd = new JobDetail(jobName, jobGroup, typeof(NoOpJob));
+ simpleTrigger.JobDetail = jd;
+ simpleTrigger.AfterPropertiesSet();
+ base.TestAfterPropertiesSet_JobDetailGiven();
+ Assert.AreSame(jd, simpleTrigger.JobDetail, "job details weren't same");
+ }
+
+ ///
+ /// Tests AfterPropertiesSet behavior.
+ ///
+ [Test]
+ public void TestJobDataAsMap()
+ {
+#if QUARTZ_2_0
+ IDictionary data = new Dictionary();
+#else
+ Hashtable data = new Hashtable();
+#endif
+ data["foo"] = "bar";
+ data["number"] = 123;
+ simpleTrigger.JobDataAsMap = data;
+ CollectionAssert.AreEquivalent(data, simpleTrigger.JobDataMap, "Data differed");
+ }
+
+ }
+
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SpringObjectJobFactoryTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SpringObjectJobFactoryTest.cs
new file mode 100644
index 00000000..97fd1587
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/SpringObjectJobFactoryTest.cs
@@ -0,0 +1,147 @@
+/*
+* Copyright 2002-2010 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.Collections;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+using Quartz;
+using Quartz.Job;
+using Quartz.Spi;
+
+#if QUARTZ_2_0
+using Trigger = Quartz.Spi.IOperableTrigger;
+using JobExecutionContext = Quartz.IJobExecutionContext;
+using JobDetail = Quartz.IJobDetail;
+using SimpleTrigger = Quartz.Impl.Triggers.SimpleTriggerImpl;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Unit tests for SpringObjectJobFactory.
+ ///
+ /// Marko Lahma (.NET)
+ [TestFixture]
+ public class SpringObjectJobFactoryTest
+ {
+ private SpringObjectJobFactory factory;
+
+ ///
+ /// Test setup.
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ factory = new SpringObjectJobFactory();
+ }
+
+ ///
+ /// Tests job instane creation.
+ ///
+ [Test]
+ public void TestCreateJobInstance_SimpleDefaults()
+ {
+ Trigger trigger = new SimpleTrigger();
+ TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof (NoOpJob), trigger);
+
+#if QUARTZ_2_0
+ IJob job = factory.NewJob(bundle, null);
+#else
+ IJob job = factory.NewJob(bundle);
+#endif
+ Assert.IsNotNull(job, "Created job was null");
+ }
+
+ ///
+ /// Tests job instane creation.
+ ///
+ [Test]
+ public void TestCreateJobInstance_SchedulerContextGiven()
+ {
+ Trigger trigger = new SimpleTrigger();
+ TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(InjectableJob), trigger);
+
+#if QUARTZ_2_0
+ IDictionary items = new Dictionary();
+ items["foo"] = "bar";
+ items["number"] = 123;
+ factory.SchedulerContext = new SchedulerContext(items);
+ InjectableJob job = (InjectableJob)factory.NewJob(bundle, null);
+#else
+ IDictionary items = new Hashtable();
+ items["foo"] = "bar";
+ items["number"] = 123;
+ factory.SchedulerContext = new SchedulerContext(items);
+ InjectableJob job = (InjectableJob) factory.NewJob(bundle);
+#endif
+
+ Assert.IsNotNull(job, "Created job was null");
+ Assert.AreEqual("bar", job.Foo, "string injection failed");
+ Assert.AreEqual(123, job.Number, "integer injection failed");
+ }
+
+ ///
+ /// Tests job instane creation.
+ ///
+ [Test]
+ public void TestCreateJobInstance_IgnoredProperties()
+ {
+ factory.IgnoredUnknownProperties = new string[] {"foo", "baz"};
+ Trigger trigger = new SimpleTrigger();
+ trigger.JobDataMap["foo"] = "should not be injected";
+ trigger.JobDataMap["number"] = 123;
+ TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(InjectableJob), trigger);
+
+#if QUARTZ_2_0
+ InjectableJob job = (InjectableJob)factory.NewJob(bundle, null);
+#else
+ InjectableJob job = (InjectableJob)factory.NewJob(bundle);
+#endif
+ Assert.IsNotNull(job, "Created job was null");
+ Assert.AreEqual(123, job.Number, "integer injection failed");
+ Assert.IsNull(job.Foo, "foo was injected when it was not supposed to ");
+ }
+
+ }
+
+ ///
+ /// Test job object that has injectable properties
+ ///
+ public class InjectableJob : NoOpJob
+ {
+ private int number;
+ private string foo;
+
+ ///
+ /// Simple int property.
+ ///
+ public int Number
+ {
+ get { return number; }
+ set { number = value; }
+ }
+
+ ///
+ /// Simple string property.
+ ///
+ public string Foo
+ {
+ get { return foo; }
+ set { foo = value; }
+ }
+ }
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TestMethodInvokingTask.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TestMethodInvokingTask.cs
new file mode 100644
index 00000000..49ff0c8a
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TestMethodInvokingTask.cs
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2002-2010 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.Threading;
+
+namespace Spring.Scheduling.Quartz
+{
+
+ ///
+ /// Simple test task.
+ ///
+ /// Juergen Hoeller
+ public class TestMethodInvokingTask
+ {
+ ///
+ /// Counter for DoSomething and DoWait calls.
+ ///
+ public int counter;
+ private readonly object lockObject = new object();
+
+ ///
+ /// Simple test method.
+ ///
+ public void DoSomething()
+ {
+ counter++;
+ }
+
+ ///
+ /// Waits until stop is called.
+ ///
+ public void DoWait()
+ {
+ counter++;
+ // wait until stop is called
+ lock (lockObject)
+ {
+ try
+ {
+ Monitor.Wait(lockObject);
+ }
+ catch (ThreadInterruptedException)
+ {
+ // fall through
+ }
+ }
+ }
+
+ ///
+ /// Informs test object that stop should be called.
+ ///
+ public void Stop()
+ {
+ lock (lockObject)
+ {
+ Monitor.Pulse(lockObject);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TestUtil.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TestUtil.cs
new file mode 100644
index 00000000..e4a13981
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TestUtil.cs
@@ -0,0 +1,60 @@
+/*
+* Copyright 2002-2010 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 Quartz;
+using Quartz.Spi;
+
+#if QUARTZ_2_0
+using JobDetail = Quartz.Impl.JobDetailImpl;
+using Trigger = Quartz.Spi.IOperableTrigger;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Quartz.NET integration testing helpers.
+ ///
+ /// Marko Lahma (.NET)
+ public class TestUtil
+ {
+ ///
+ /// Creates the minimal fired bundle with job detail that has
+ /// given job type.
+ ///
+ /// Type of the job.
+ /// Minimal TriggerFiredBundle
+ public static TriggerFiredBundle CreateMinimalFiredBundleWithTypedJobDetail(Type jobType)
+ {
+ return CreateMinimalFiredBundleWithTypedJobDetail(jobType, null);
+ }
+
+ ///
+ /// Creates the minimal fired bundle with job detail that has
+ /// given job type.
+ ///
+ /// Type of the job.
+ /// The trigger.
+ /// Minimal TriggerFiredBundle
+ public static TriggerFiredBundle CreateMinimalFiredBundleWithTypedJobDetail(Type jobType, Trigger trigger)
+ {
+ JobDetail jd = new JobDetail("jobName", "jobGroup", jobType);
+ TriggerFiredBundle bundle = new TriggerFiredBundle(jd, trigger, null, false, null, null, null, null);
+ return bundle;
+ }
+ }
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TriggerObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TriggerObjectTest.cs
new file mode 100644
index 00000000..f3bd2af9
--- /dev/null
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Scheduling/Quartz/TriggerObjectTest.cs
@@ -0,0 +1,159 @@
+/*
+* Copyright 2002-2010 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 NUnit.Framework;
+
+using Quartz;
+
+using Spring.Objects.Factory;
+
+#if QUARTZ_2_0
+using Trigger = Quartz.Spi.IOperableTrigger;
+#endif
+
+namespace Spring.Scheduling.Quartz
+{
+ ///
+ /// Base class for testing triggers. Contains common functionality.
+ ///
+ [TestFixture]
+ public abstract class TriggerObjectTest
+ {
+ private Trigger trigger;
+
+ ///
+ /// Constant name for tested triggers.
+ ///
+ protected const string TRIGGER_NAME = "trigger";
+
+ ///
+ /// TriggerObject under test.
+ ///
+ protected Trigger Trigger
+ {
+ set { trigger = value; }
+ }
+
+ ///
+ /// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
+ ///
+ [Test]
+ public virtual void TestAfterPropertiesSet_Defaults()
+ {
+ ((IInitializingObject) trigger).AfterPropertiesSet();
+
+#if QUARTZ_2_0
+ Assert.AreEqual(TRIGGER_NAME, trigger.Key.Name, "trigger name mismatch");
+ Assert.AreEqual(SchedulerConstants.DefaultGroup, trigger.Key.Group, "trigger group name mismatch");
+ Assert.IsNull(trigger.JobKey, "trigger job name not null");
+#else
+ Assert.AreEqual(TRIGGER_NAME, trigger.Name, "trigger name mismatch");
+ Assert.AreEqual(SchedulerConstants.DefaultGroup, trigger.Group, "trigger group name mismatch");
+ Assert.IsNull(trigger.JobName, "trigger job name not null");
+ Assert.AreEqual(SchedulerConstants.DefaultGroup, trigger.JobGroup, "trigger job group was not default");
+#endif
+ AssertDateTimesEqualityWithAllowedDelta(DateTime.UtcNow, trigger.StartTimeUtc, 1000);
+ }
+
+ ///
+ /// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
+ ///
+ [Test]
+ public virtual void TestAfterPropertiesSet_ValuesGiven()
+ {
+ ((IInitializingObject)trigger).AfterPropertiesSet();
+
+ const string NAME = "newName";
+ const string GROUP = "newGroup";
+ DateTime START_TIME = new DateTime(1982, 6, 28, 13, 10, 0);
+ trigger.StartTimeUtc = START_TIME;
+#if QUARTZ_2_0
+ trigger.Key = new TriggerKey(NAME, GROUP);
+ Assert.AreEqual(NAME, trigger.Key.Name, "trigger name mismatch");
+ Assert.AreEqual(GROUP, trigger.Key.Group, "trigger group name mismatch");
+#else
+ trigger.Name = NAME;
+ trigger.Group = GROUP;
+ Assert.AreEqual(NAME, trigger.Name, "trigger name mismatch");
+ Assert.AreEqual(GROUP, trigger.Group, "trigger group name mismatch");
+#endif
+ AssertDateTimesEqualityWithAllowedDelta(START_TIME, trigger.StartTimeUtc, 1000);
+ }
+
+ ///
+ /// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
+ ///
+ [Test]
+ public virtual void TestAfterPropertiesSet_JobDetailGiven()
+ {
+ ((IInitializingObject)trigger).AfterPropertiesSet();
+
+ const string jobName = "jobName";
+ const string jobGroup = "jobGroup";
+#if QUARTZ_2_0
+ Assert.AreEqual(jobName, trigger.JobKey.Name, "trigger job name was not from job detail");
+ Assert.AreEqual(jobGroup, trigger.JobKey.Group, "trigger job group was not from job detail");
+#else
+ Assert.AreEqual(jobName, trigger.JobName, "trigger job name was not from job detail");
+ Assert.AreEqual(jobGroup, trigger.JobGroup, "trigger job group was not from job detail");
+#endif
+ }
+
+#if QUARTZ_2_0
+ ///
+ /// Tests whether two datetimes are close enough.
+ ///
+ ///
+ ///
+ ///
+ protected static void AssertDateTimesEqualityWithAllowedDelta(DateTimeOffset d1, DateTimeOffset d2, int allowedDeltaInMilliseconds)
+ {
+ int diffInMillis = (int) Math.Abs((d1 - d2).TotalMilliseconds);
+ Assert.LessOrEqual(diffInMillis, allowedDeltaInMilliseconds, "too much difference in times");
+ }
+#else
+
+ ///
+ /// Tests whether two datetimes are close enough.
+ ///
+ ///
+ ///
+ ///
+ protected static void AssertDateTimesEqualityWithAllowedDelta(DateTime d1, DateTime d2, int allowedDeltaInMilliseconds)
+ {
+ int diffInMillis = (int)Math.Abs((d1 - d2).TotalMilliseconds);
+ Assert.LessOrEqual(diffInMillis, allowedDeltaInMilliseconds, "too much difference in times");
+ }
+
+
+ ///
+ /// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
+ ///
+ [Test]
+ public virtual void TestTriggerListenerNames_Valid()
+ {
+ ((IInitializingObject)trigger).AfterPropertiesSet();
+
+ string[] LISTENER_NAMES = new string[] { "Foo", "Bar", "Baz" };
+ trigger.TriggerListenerNames = LISTENER_NAMES;
+ CollectionAssert.AreEqual(LISTENER_NAMES, trigger.TriggerListenerNames, "Trigger listeners were not equal");
+ }
+#endif
+ }
+
+}
diff --git a/test/Spring/Spring.Scheduling.Quartz20.Tests/Spring.Scheduling.Quartz20.Tests.2010.csproj b/test/Spring/Spring.Scheduling.Quartz20.Tests/Spring.Scheduling.Quartz20.Tests.2010.csproj
index ec550741..0a94ad29 100644
--- a/test/Spring/Spring.Scheduling.Quartz20.Tests/Spring.Scheduling.Quartz20.Tests.2010.csproj
+++ b/test/Spring/Spring.Scheduling.Quartz20.Tests/Spring.Scheduling.Quartz20.Tests.2010.csproj
@@ -71,50 +71,6 @@
Spring.Core.Tests.2010
-
-
- QuartzCompilerOptionsTests.cs
-
-
- Scheduling\Quartz\AdaptableJobFactoryTest.cs
-
-
- Scheduling\Quartz\CronTriggerObjectTest.cs
-
-
- Scheduling\Quartz\JobDetailObjectTest.cs
-
-
- Scheduling\Quartz\MethodInvokingJobDetailFactoryObjectTest.cs
-
-
- Scheduling\Quartz\MethodInvokingJobTest.cs
-
-
- Scheduling\Quartz\QuartzSupportTests.cs
-
-
- Scheduling\Quartz\QuartzTestObject.cs
-
-
- Scheduling\Quartz\SchedulerFactoryObjectTest.cs
-
-
- Scheduling\Quartz\SimpleTriggerObjectTest.cs
-
-
- Scheduling\Quartz\SpringObjectJobFactoryTest.cs
-
-
- Scheduling\Quartz\TestMethodInvokingTask.cs
-
-
- Scheduling\Quartz\TestUtil.cs
-
-
- Scheduling\Quartz\TriggerObjectTest.cs
-
-
PreserveNewest
@@ -137,6 +93,23 @@
PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+