removing refs to linking of files from Quartz1x projects

This commit is contained in:
Steve Bohlen
2012-05-11 09:05:37 -04:00
parent a3bc044c96
commit 416d934714
35 changed files with 5758 additions and 101 deletions

View File

@@ -0,0 +1,46 @@
/*
* 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 Spring.Objects.Support;
namespace Spring.Scheduling.Quartz
{
/// <summary>
/// Exception that wraps an exception thrown from a target method.
/// Propagated to the Quartz scheduler from a Job that reflectively invokes
/// an arbitrary target method.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <seealso cref="MethodInvokingJobDetailFactoryObject" />
public class JobMethodInvocationFailedException : Exception // TODO, in Java NestedRuntimeException
{
/// <summary>
/// Constructor for JobMethodInvocationFailedException.
/// </summary>
/// <param name="methodInvoker">the MethodInvoker used for reflective invocation</param>
/// <param name="cause">the root cause (as thrown from the target method)</param>
public JobMethodInvocationFailedException(MethodInvoker methodInvoker, Exception cause) :
base("Invocation of method '" + methodInvoker.TargetMethod +
"' on target class [" + methodInvoker.TargetType + "] failed", cause)
{
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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.Spi;
using Quartz.Util;
#if QUARTZ_2_0
using JobDetail = Quartz.IJobDetail;
#else
using JobDetail = Quartz.JobDetail;
#endif
namespace Spring.Scheduling.Quartz
{
/// <summary>
/// JobFactory implementation that supports <see cref="ThreadStart" />
/// objects as well as standard Quartz <see cref="IJob" /> instances.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Marko Lahma (.NET)</author>
/// <seealso cref="DelegatingJob" />
/// <seealso cref="AdaptJob(object)" />
public class AdaptableJobFactory : IJobFactory
{
#if QUARTZ_2_0
/// <summary>
/// Called by the scheduler at the time of the trigger firing, in order to
/// produce a <see cref="IJob"/> instance on which to call Execute.
/// </summary>
/// <remarks>
/// It should be extremely rare for this method to throw an exception -
/// basically only the the case where there is no way at all to instantiate
/// and prepare the Job for execution. When the exception is thrown, the
/// Scheduler will move all triggers associated with the Job into the
/// <see cref="TriggerState.Error"/> state, which will require human
/// intervention (e.g. an application restart after fixing whatever
/// configuration problem led to the issue wih instantiating the Job.
/// </remarks>
/// <param name="bundle">The TriggerFiredBundle from which the <see cref="JobDetail"/>
/// and other info relating to the trigger firing can be obtained.</param>
/// <param name="scheduler">The scheduler instance.</param>
/// <returns>the newly instantiated Job</returns>
/// <throws>SchedulerException if there is a problem instantiating the Job.</throws>
public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
#else
/// <summary>
/// Called by the scheduler at the time of the trigger firing, in order to
/// produce a <see cref="IJob"/> instance on which to call Execute.
/// </summary>
/// <remarks>
/// It should be extremely rare for this method to throw an exception -
/// basically only the the case where there is no way at all to instantiate
/// and prepare the Job for execution. When the exception is thrown, the
/// Scheduler will move all triggers associated with the Job into the
/// <see cref="TriggerState.Error"/> state, which will require human
/// intervention (e.g. an application restart after fixing whatever
/// configuration problem led to the issue wih instantiating the Job.
/// </remarks>
/// <param name="bundle">The TriggerFiredBundle from which the <see cref="JobDetail"/>
/// and other info relating to the trigger firing can be obtained.</param>
/// <returns>the newly instantiated Job</returns>
/// <throws>SchedulerException if there is a problem instantiating the Job.</throws>
public virtual IJob NewJob(TriggerFiredBundle bundle)
#endif
{
try
{
object jobObject = CreateJobInstance(bundle);
return AdaptJob(jobObject);
}
catch (Exception ex)
{
throw new SchedulerException("Job instantiation failed", ex);
}
}
/// <summary>
/// Create an instance of the specified job class.
/// <p>
/// Can be overridden to post-process the job instance.
/// </p>
/// </summary>
/// <param name="bundle">
/// The TriggerFiredBundle from which the JobDetail
/// and other info relating to the trigger firing can be obtained.
/// </param>
/// <returns>The job instance.</returns>
protected virtual object CreateJobInstance(TriggerFiredBundle bundle)
{
#if QUARTZ_2_0
return ObjectUtils.InstantiateType<object>(bundle.JobDetail.JobType);
#else
return ObjectUtils.InstantiateType(bundle.JobDetail.JobType);
#endif
}
/// <summary>
/// Adapt the given job object to the Quartz Job interface.
/// </summary>
/// <remarks>
/// The default implementation supports straight Quartz Jobs
/// as well as Runnables, which get wrapped in a DelegatingJob.
/// </remarks>
/// <param name="jobObject">
/// The original instance of the specified job class.
/// </param>
/// <returns>The adapted Quartz Job instance.</returns>
/// <seealso cref="DelegatingJob" />
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);
}
}
}

View File

@@ -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
{
/// <summary>
/// Simple Quartz IJob adapter that delegates to a
/// given <see cref="ThreadStart" /> instance.
/// </summary>
/// <remarks>
/// Typically used in combination with property injection on the
/// Runnable instance, receiving parameters from the Quartz JobDataMap
/// that way instead of via the JobExecutionContext.
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Marko Lahma (.NET)</author>
/// <seealso cref="SpringObjectJobFactory" />
/// <seealso cref="IJob.Execute(JobExecutionContext)" />
public class DelegatingJob : IJob
{
private readonly ThreadStart delegateInstance;
/// <summary>
/// Return the wrapped Runnable implementation.
/// </summary>
/// <value>The delegate.</value>
public virtual ThreadStart Delegate
{
get { return delegateInstance; }
}
/// <summary>
/// Create a new DelegatingJob.
/// </summary>
/// <param name="delegateInstance">
/// The Runnable implementation to delegate to.
/// </param>
public DelegatingJob(ThreadStart delegateInstance)
{
AssertUtils.ArgumentNotNull(delegateInstance, "delegateInstance", "Delegate must not be null");
this.delegateInstance = delegateInstance;
}
/// <summary>
/// Delegates execution to the underlying ThreadStart.
/// </summary>
public virtual void Execute(JobExecutionContext context)
{
delegateInstance.Invoke();
}
}
}

View File

@@ -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
{
/// <summary>
/// Interface to be implemented by Quartz Triggers that are aware
/// of the JobDetail object that they are associated with.
/// </summary>
/// <remarks>
/// <p>
/// SchedulerFactoryObject will auto-detect Triggers that implement this
/// interface and register them for the respective JobDetail accordingly.
/// </p>
///
/// <p>
/// 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.
/// </p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <seealso cref="SchedulerAccessor.Triggers" />
/// <seealso cref="SchedulerAccessor.JobDetails" />
public interface IJobDetailAwareTrigger
{
/// <summary>
/// Return the JobDetail that this Trigger is associated with.
/// </summary>
/// <returns>The associated JobDetail, or <code>null</code> if none</returns>
JobDetail JobDetail { get; }
}
}

View File

@@ -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
{
/// <summary>
/// Callback interface to be implemented by Spring-managed
/// Quartz artifacts that need access to the SchedulerContext
/// (without having natural access to it).
/// </summary>
/// <remarks>
/// Currently only supported for custom JobFactory implementations
/// that are passed in via Spring's SchedulerFactoryObject.
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <seealso cref="IJobFactory" />
/// <seealso cref="SchedulerFactoryObject.JobFactory" />
public interface ISchedulerContextAware
{
/// <summary>
/// Set the SchedulerContext of the current Quartz Scheduler.
/// </summary>
/// <seealso cref="IScheduler.Context" />
SchedulerContext SchedulerContext { set; }
}
}

View File

@@ -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
{
/// <summary>
///
/// </summary>
public interface ISchedulingTaskExecutor : ITaskExecutor
{
/// <summary>
/// Gets a value indicating whether<65>this instance prefers short lived tasks.
/// </summary>
/// <value>
/// <c>true</c> if prefers short lived tasks; otherwise, <c>false</c>.
/// </value>
bool PrefersShortLivedTasks { get; }
}
}

View File

@@ -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
{
/// <summary>
///
/// </summary>
public interface ITaskExecutor
{
/// <summary>
/// Executes this instance.
/// </summary>
void Execute(ThreadStart runnable);
}
}

View File

@@ -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
{
/// <summary>
/// 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.
///</summary>
/// <remarks>
/// <p>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.</p>
///
/// <p>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.</p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Marko Lahma (.NET)</author>
/// <seealso cref="ConnectionUtils.GetConnection" />
/// <seealso cref="ConnectionUtils.DisposeConnection" />
public class LocalDataSourceJobStore : JobStoreCMT
{
/// <summary>
/// Name used for the transactional ConnectionProvider for Quartz.
/// This provider will delegate to the local Spring-managed DataSource.
/// <seealso cref="DBConnectionManager.AddConnectionProvider" />
/// <seealso cref="SchedulerFactoryObject.DbProvider" />
/// </summary>
public const string TX_DATA_SOURCE_PREFIX = "springTxDataSource.";
private Data.Common.IDbProvider dbProvider;
/// <summary>
/// Gets or sets the name of the instance.
/// </summary>
/// <value>The name of the instance.</value>
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));
}
}
/// <summary>
/// Gets the non managed TX connection.
/// </summary>
/// <returns></returns>
protected override ConnectionAndTransactionHolder GetNonManagedTXConnection()
{
ConnectionTxPair pair = ConnectionUtils.DoGetConnection(dbProvider);
return new ConnectionAndTransactionHolder(pair.Connection, pair.Transaction);
}
/// <summary>
/// Closes the connection.
/// </summary>
/// <param name="connectionAndTransactionHolder">The connection and transaction holder.</param>
protected override void CloseConnection(ConnectionAndTransactionHolder connectionAndTransactionHolder)
{
// Will work for transactional and non-transactional connections.
ConnectionUtils.DisposeConnection(connectionAndTransactionHolder.Connection, dbProvider);
}
}
}

View File

@@ -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
{
/// <summary>
/// Quartz ThreadPool adapter that delegates to a Spring-managed
/// TaskExecutor instance, specified on SchedulerFactoryObject.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <seealso cref="SchedulerFactoryObject.TaskExecutor" />
public class LocalTaskExecutorThreadPool : IThreadPool
{
/// <summary>
/// Logger available to subclasses.
/// </summary>
private readonly ILog logger;
private ITaskExecutor taskExecutor;
/// <summary>
/// Initializes a new instance of the <see cref="LocalTaskExecutorThreadPool"/> class.
/// </summary>
public LocalTaskExecutorThreadPool()
{
logger = LogManager.GetLogger(GetType());
}
/// <summary>
/// Logger instance.
/// </summary>
protected ILog Logger
{
get { return logger; }
}
/// <summary>
/// Gets the size of the pool.
/// </summary>
/// <value>The size of the pool.</value>
public virtual int PoolSize
{
get { return - 1; }
}
/// <summary>
/// Inform the <see cref="T:Quartz.Spi.IThreadPool"/> of the Scheduler instance's Id,
/// prior to initialize being invoked.
/// </summary>
public string InstanceId
{
set { }
}
/// <summary>
/// Inform the <see cref="T:Quartz.Spi.IThreadPool"/> of the Scheduler instance's name,
/// prior to initialize being invoked.
/// </summary>
public string InstanceName
{
set { }
}
/// <summary>
/// Called by the QuartzScheduler before the <see cref="T:System.Threading.ThreadPool"/> is
/// used, in order to give the it a chance to Initialize.
/// </summary>
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");
}
}
/// <summary>
/// Called by the QuartzScheduler to inform the <see cref="T:System.Threading.ThreadPool"/>
/// that it should free up all of it's resources because the scheduler is
/// shutting down.
/// </summary>
/// <param name="waitForJobsToComplete"></param>
public virtual void Shutdown(bool waitForJobsToComplete)
{
}
/// <summary>
/// Execute the given <see cref="T:Quartz.IThreadRunnable"/> in the next
/// available <see cref="T:System.Threading.Thread"/>.
/// </summary>
/// <param name="runnable"></param>
/// <returns></returns>
/// <remarks>
/// 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.
/// </remarks>
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;
}
}
/// <summary>
/// Determines the number of threads that are currently available in in
/// the pool. Useful for determining the number of times
/// <see cref="M:Quartz.Spi.IThreadPool.RunInThread(Quartz.IThreadRunnable)"/> can be called before returning
/// false.
/// </summary>
/// <returns>
/// the number of currently available threads
/// </returns>
/// <remarks>
/// The implementation of this method should block until there is at
/// least one available thread.
/// </remarks>
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 <code>getMaximumPoolSize() - getActiveCount()</code>
// on a <code>java.util.concurrent.ThreadPoolExecutor</code>.
return 1;
}
}
}

View File

@@ -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
{
/// <summary>
/// Quartz Job implementation that invokes a specified method.
/// Automatically applied by MethodInvokingJobDetailFactoryObject.
/// </summary>
public class MethodInvokingJob : QuartzJobObject
{
private static readonly ILog logger = LogManager.GetLogger(typeof(MethodInvokingJob));
private MethodInvoker methodInvoker;
private string errorMessage;
/// <summary>
/// Set the MethodInvoker to use.
/// </summary>
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);
}
}
/// <summary>
/// Invoke the method via the MethodInvoker.
/// </summary>
/// <param name="context"></param>
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());
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Adapter that implements the Runnable interface as a configurable
/// method invocation based on Spring's MethodInvoker.
/// </summary>
/// <remarks>
/// <p>
/// Derives from ArgumentConvertingMethodInvoker, inheriting common
/// configuration properties from MethodInvoker.
/// </p>
///
/// <p>
/// Useful to generically encapsulate a method invocation as timer task for
/// <code>java.util.Timer</code>, in combination with a DelegatingTimerTask adapter.
/// Can also be used with JDK 1.5's <code>java.util.concurrent.Executor</code>
/// abstraction, which works with plain Runnables.
/// </p>
/// <p>
/// Extended by Spring's MethodInvokingTimerTaskFactoryObject adapter
/// for <code>TimerTask</code>. Note that you can populate a
/// ScheduledTimerTask object with a plain MethodInvokingRunnable instance
/// as well, which will automatically get wrapped with a DelegatingTimerTask.
/// </p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <seealso cref="MethodInvoker" />
/// <seealso cref="ArgumentConvertingMethodInvoker" />
public class MethodInvokingRunnable : ArgumentConvertingMethodInvoker, IInitializingObject, IThreadRunnable
{
/// <summary>
/// Logger instance shared by this instance and its sub-class instances.
/// </summary>
private readonly ILog logger;
/// <summary>
/// Initializes a new instance of the <see cref="MethodInvokingRunnable"/> class.
/// </summary>
public MethodInvokingRunnable()
{
logger = LogManager.GetLogger(GetType());
}
/// <summary>
/// Logger instance.
/// </summary>
protected ILog Logger
{
get { return logger; }
}
/// <summary>
/// Gets the invocation failure message.
/// </summary>
/// <value>The invocation failure message.</value>
protected virtual string InvocationFailureMessage
{
get { return string.Format("Invocation of method '{0}' on target object [{1}] failed", TargetMethod, TargetObject); }
}
/// <summary>
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has injected all of an object's dependencies.
/// </summary>
/// <remarks>
/// <p>
/// This method allows the object instance to perform the kind of
/// initialization only possible when all of it's dependencies have
/// been injected (set), and to throw an appropriate exception in the
/// event of misconfiguration.
/// </p>
/// <p>
/// Please do consult the class level documentation for the
/// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
/// description of exactly <i>when</i> this method is invoked. In
/// particular, it is worth noting that the
/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
/// and <see cref="Spring.Context.IApplicationContextAware"/>
/// callbacks will have been invoked <i>prior</i> to this method being
/// called.
/// </p>
/// </remarks>
/// <exception cref="System.Exception">
/// In the event of misconfiguration (such as the failure to set a
/// required property) or if initialization fails.
/// </exception>
public virtual void AfterPropertiesSet()
{
Prepare();
}
/// <summary>
/// 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.
/// </summary>
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!
}
}
}
}

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// <p>
/// 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.
/// </p>
///
/// <p>
/// 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.
/// </p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <seealso cref="JobExecutionContext.MergedJobDataMap" />
/// <seealso cref="IScheduler.Context" />
/// <seealso cref="JobDetailObject.JobDataAsMap" />
/// <seealso cref="CronTriggerObject.JobDataAsMap" />
/// <seealso cref="SchedulerFactoryObject.SchedulerContextAsMap" />
/// <seealso cref="SpringObjectJobFactory" />
/// <seealso cref="SchedulerFactoryObject.JobFactory" />
public abstract class QuartzJobObject : IJob
{
/// <summary>
/// This implementation applies the passed-in job data map as object property
/// values, and delegates to <code>ExecuteInternal</code> afterwards.
/// </summary>
/// <seealso cref="ExecuteInternal" />
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);
}
/// <summary>
/// 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.
/// </summary>
/// <seealso cref="Execute" />
protected abstract void ExecuteInternal(JobExecutionContext context);
}
}

View File

@@ -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
{
/// <summary>
/// Spring class for accessing a Quartz Scheduler, i.e. for registering jobs,
/// triggers and listeners on a given <see cref="IScheduler" /> instance.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Marko Lahma (.NET)</author>
/// <seealso cref="Scheduler" />
/// <seealso cref="SchedulerName" />
public class SchedulerAccessorObject : SchedulerAccessor, IObjectFactoryAware, IInitializingObject
{
private string schedulerName;
private IScheduler scheduler;
private IObjectFactory objectFactory;
/// <summary>
/// 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}.
/// </summary>
/// <remarks>
/// Schedulers can be registered in the repository through custom bootstrapping,
/// e.g. via the <see cref="StdSchedulerFactory" /> or
/// <see cref="DirectSchedulerFactory" /> factory classes.
/// However, in general, it's preferable to use Spring's <see cref="SchedulerFactoryObject" />
/// which includes the job/trigger/listener capabilities of this accessor as well.
/// </remarks>
public string SchedulerName
{
set { schedulerName = value; }
}
/// <summary>
/// Return the Quartz Scheduler instance that this accessor operates on.
/// </summary>
protected IScheduler Scheduler
{
set { scheduler = value; }
}
/// <summary>
/// Template method that determines the Scheduler to operate on.
/// </summary>
/// <returns></returns>
protected override IScheduler GetScheduler()
{
return scheduler;
}
/// <summary>
/// Return the Quartz Scheduler instance that this accessor operates on.
/// </summary>
public IObjectFactory ObjectFactory
{
set { objectFactory = value; }
}
/// <summary>
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has injected all of an object's dependencies.
/// </summary>
/// <remarks>
/// <p>
/// This method allows the object instance to perform the kind of
/// initialization only possible when all of it's dependencies have
/// been injected (set), and to throw an appropriate exception in the
/// event of misconfiguration.
/// </p>
/// <p>
/// Please do consult the class level documentation for the
/// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
/// description of exactly <i>when</i> this method is invoked. In
/// particular, it is worth noting that the
/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
/// and <see cref="Spring.Context.IApplicationContextAware"/>
/// callbacks will have been invoked <i>prior</i> to this method being
/// called.
/// </p>
/// </remarks>
/// <exception cref="System.Exception">
/// In the event of misconfiguration (such as the failure to set a
/// required property) or if initialization fails.
/// </exception>
public void AfterPropertiesSet()
{
if (scheduler == null)
{
if (schedulerName != null)
{
scheduler = FindScheduler(schedulerName);
}
else
{
throw new InvalidOperationException("No Scheduler specified");
}
}
RegisterListeners();
RegisterJobsAndTriggers();
}
/// <summary>
/// Finds the scheduler.
/// </summary>
/// <param name="schedulerName">Name of the scheduler.</param>
/// <returns></returns>
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;
}
}
}

View File

@@ -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
{
/// <summary>
/// FactoryObject that sets up a Quartz Scheduler and exposes it for object references.
/// </summary>
/// <remarks>
/// <p>
/// Allows registration of JobDetails, Calendars and Triggers, automatically
/// starting the scheduler on initialization and shutting it down on destruction.
/// In scenarios that just require static registration of jobs at startup, there
/// is no need to access the Scheduler instance itself in application code.
/// </p>
///
/// <p>
/// For dynamic registration of jobs at runtime, use a object reference to
/// this SchedulerFactoryObject to get direct access to the Quartz Scheduler
/// (<see cref="IScheduler" />). This allows you to create new jobs
/// and triggers, and also to control and monitor the entire Scheduler.
/// </p>
///
/// <p>
/// Note that Quartz instantiates a new Job for each execution, in
/// contrast to Timer which uses a TimerTask instance that is shared
/// between repeated executions. Just JobDetail descriptors are shared.
/// </p>
///
/// <p>
/// When using persistent jobs, it is strongly recommended to perform all
/// operations on the Scheduler within Spring-managed transactions.
/// Else, database locking will not properly work and might even break.
/// </p>
/// <p>
/// The preferred way to achieve transactional execution is to demarcate
/// declarative transactions at the business facade level, which will
/// automatically apply to Scheduler operations performed within those scopes.
/// Alternatively, define a TransactionProxyFactoryObject for the Scheduler itself.
/// </p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Marko Lahma (.NET)</author>
/// <seealso cref="IScheduler" />
/// <seealso cref="ISchedulerFactory" />
/// <seealso cref="StdSchedulerFactory" />
public class SchedulerFactoryObject : SchedulerAccessor, IFactoryObject, IObjectNameAware,
IApplicationContextAware, IApplicationEventListener, IInitializingObject, IDisposable
{
/// <summary>
/// Default thread count to be set to thread pool.
/// </summary>
public const int DEFAULT_THREAD_COUNT = 10;
/// <summary>
/// Property name for thread count in thread pool.
/// </summary>
public const string PROP_THREAD_COUNT = "quartz.threadPool.threadCount";
[ThreadStatic]
private static IDbProvider configTimeDbProvider;
[ThreadStatic]
private static ITaskExecutor configTimeTaskExecutor;
/// <summary>
/// Return the IDbProvider for the currently configured Quartz Scheduler,
/// to be used by LocalDataSourceJobStore.
/// </summary>
/// <remarks>
/// This instance will be set before initialization of the corresponding
/// Scheduler, and reset immediately afterwards. It is thus only available
/// during configuration.
/// </remarks>
/// <seealso cref="DbProvider" />
/// <seealso cref="LocalDataSourceJobStore" />
public static IDbProvider ConfigTimeDbProvider
{
get { return configTimeDbProvider; }
}
/// <summary>
/// Return the TaskExecutor for the currently configured Quartz Scheduler,
/// to be used by LocalTaskExecutorThreadPool.
/// </summary>
/// <remarks>
/// This instance will be set before initialization of the corresponding
/// Scheduler, and reset immediately afterwards. It is thus only available
/// during configuration.
/// </remarks>
public static ITaskExecutor ConfigTimeTaskExecutor
{
get { return configTimeTaskExecutor; }
}
private IApplicationContext applicationContext;
private string applicationContextSchedulerContextKey;
private bool autoStartup = true;
private IResource configLocation;
private IJobFactory jobFactory;
private bool jobFactorySet;
private IDictionary quartzProperties;
private IScheduler scheduler;
private IDictionary schedulerContextMap;
private Type schedulerFactoryType;
private string schedulerName;
private TimeSpan startupDelay = TimeSpan.Zero;
private ITaskExecutor taskExecutor;
private bool exposeSchedulerInRepository;
private bool waitForJobsToCompleteOnShutdown;
private IDbProvider dbProvider;
/// <summary>
/// Initializes a new instance of the <see cref="SchedulerFactoryObject"/> class.
/// </summary>
public SchedulerFactoryObject()
{
schedulerFactoryType = typeof (StdSchedulerFactory);
}
/// <summary>
/// Set the Quartz SchedulerFactory implementation to use.
/// </summary>
/// <remarks>
/// Default is StdSchedulerFactory, reading in the standard
/// quartz.properties from Quartz' dll. To use custom Quartz
/// properties, specify "configLocation" or "quartzProperties".
/// </remarks>
/// <value>The scheduler factory class.</value>
/// <seealso cref="StdSchedulerFactory"/>
/// <seealso cref="ConfigLocation"/>
/// <seealso cref="QuartzProperties"/>
public virtual Type SchedulerFactoryType
{
set
{
if (value == null || !typeof (ISchedulerFactory).IsAssignableFrom(value))
{
throw new ArgumentException("schedulerFactoryType must implement [Quartz.ISchedulerFactory]");
}
schedulerFactoryType = value;
}
}
/// <summary>
/// Set the name of the Scheduler to fetch from the SchedulerFactory.
/// If not specified, the default Scheduler will be used.
/// </summary>
/// <value>The name of the scheduler.</value>
/// <seealso cref="ISchedulerFactory.GetScheduler(string)"/>
/// <seealso cref="ISchedulerFactory.GetScheduler()"/>
public virtual string SchedulerName
{
set { schedulerName = value; }
}
/// <summary>
/// Set the location of the Quartz properties config file, for example
/// as assembly resource "assembly:quartz.properties".
/// </summary>
/// <remarks>
/// Note: Can be omitted when all necessary properties are specified
/// locally via this object, or when relying on Quartz' default configuration.
/// </remarks>
/// <seealso cref="QuartzProperties" />
public virtual IResource ConfigLocation
{
set { configLocation = value; }
}
/// <summary>
/// Set Quartz properties, like "quartz.threadPool.type".
/// </summary>
/// <remarks>
/// Can be used to override values in a Quartz properties config file,
/// or to specify all necessary properties locally.
/// </remarks>
/// <seealso cref="ConfigLocation" />
public virtual IDictionary QuartzProperties
{
set { quartzProperties = value; }
}
/// <summary>
/// Set the Spring TaskExecutor to use as Quartz backend.
/// Exposed as thread pool through the Quartz SPI.
/// </summary>
/// <remarks>
/// By default, a Quartz SimpleThreadPool will be used, configured through
/// the corresponding Quartz properties.
/// </remarks>
/// <value>The task executor.</value>
/// <seealso cref="QuartzProperties"/>
/// <seealso cref="LocalTaskExecutorThreadPool"/>
public virtual ITaskExecutor TaskExecutor
{
set { taskExecutor = value; }
}
/// <summary>
/// Register objects in the Scheduler context via a given Map.
/// These objects will be available to any Job that runs in this Scheduler.
/// </summary>
/// <remarks>
/// Note: When using persistent Jobs whose JobDetail will be kept in the
/// database, do not put Spring-managed object or an ApplicationContext
/// reference into the JobDataMap but rather into the SchedulerContext.
/// </remarks>
/// <value>
/// Map with string keys and any objects as
/// values (for example Spring-managed objects)
/// </value>
/// <seealso cref="JobDetailObject.JobDataAsMap" />
public virtual IDictionary SchedulerContextAsMap
{
set { schedulerContextMap = value; }
}
/// <summary>
/// Set the key of an IApplicationContext reference to expose in the
/// SchedulerContext, for example "applicationContext". Default is none.
/// Only applicable when running in a Spring ApplicationContext.
/// </summary>
/// <remarks>
/// <p>
/// Note: When using persistent Jobs whose JobDetail will be kept in the
/// database, do not put an IApplicationContext reference into the JobDataMap
/// but rather into the SchedulerContext.
/// </p>
///
/// <p>
/// In case of a QuartzJobObject, the reference will be applied to the Job
/// instance as object property. An "applicationContext" attribute will
/// correspond to a "setApplicationContext" method in that scenario.
/// </p>
///
/// <p>
/// Note that ObjectFactory callback interfaces like IApplicationContextAware
/// are not automatically applied to Quartz Job instances, because Quartz
/// itself is reponsible for the lifecycle of its Jobs.
/// </p>
/// </remarks>
/// <value>The application context scheduler context key.</value>
/// <seealso cref="JobDetailObject.ApplicationContextJobDataKey"/>
public virtual string ApplicationContextSchedulerContextKey
{
set { applicationContextSchedulerContextKey = value; }
}
/// <summary>
/// Set the Quartz JobFactory to use for this Scheduler.
/// </summary>
/// <remarks>
/// <p>
/// Default is Spring's <see cref="AdaptableJobFactory" />, which supports
/// standard Quartz <see cref="IJob" /> instances. Note that this default only applies
/// to a <i>local</i> Scheduler, not to a RemoteScheduler (where setting
/// a custom JobFactory is not supported by Quartz).
/// </p>
/// <p>
/// Specify an instance of Spring's <see cref="SpringObjectJobFactory" /> here
/// (typically as an inner object definition) to automatically populate a job's
/// object properties from the specified job data map and scheduler context.
/// </p>
/// </remarks>
/// <seealso cref="AdaptableJobFactory" />
/// <seealso cref="SpringObjectJobFactory" />
public virtual IJobFactory JobFactory
{
set
{
jobFactory = value;
jobFactorySet = true;
}
}
/// <summary>
/// Set whether to expose the Spring-managed <see cref="IScheduler" /> instance in the
/// Quartz <see cref="SchedulerRepository" />. Default is "false", since the Spring-managed
/// Scheduler is usually exclusively intended for access within the Spring context.
/// </summary>
/// <remarks>
/// Switch this flag to "true" in order to expose the Scheduler globally.
/// This is not recommended unless you have an existing Spring application that
/// relies on this behavior.
/// </remarks>
public virtual bool ExposeSchedulerInRepository
{
set { exposeSchedulerInRepository = value; }
}
/// <summary>
/// Set whether to automatically start the scheduler after initialization.
/// Default is "true"; set this to "false" to allow for manual startup.
/// </summary>
public virtual bool AutoStartup
{
set { autoStartup = value; }
}
/// <summary>
/// Set the time span to wait after initialization before
/// starting the scheduler asynchronously. Default is 0, meaning
/// immediate synchronous startup on initialization of this object.
/// </summary>
/// <remarks>
/// Setting this to 10 or 20 seconds makes sense if no jobs
/// should be run before the entire application has started up.
/// </remarks>
public virtual TimeSpan StartupDelay
{
set { startupDelay = value; }
}
/// <summary>
/// Set whether to wait for running jobs to complete on Shutdown.
/// Default is "false".
/// </summary>
/// <value>
/// <c>true</c> if [wait for jobs to complete on Shutdown]; otherwise, <c>false</c>.
/// </value>
/// <seealso cref="IScheduler.Shutdown(bool)"/>
public virtual bool WaitForJobsToCompleteOnShutdown
{
set { waitForJobsToCompleteOnShutdown = value; }
}
/// <summary>
/// Set the default DbProvider to be used by the Scheduler. If set,
/// this will override corresponding settings in Quartz properties.
/// </summary>
/// <remarks>
/// <p>
/// Note: If this is set, the Quartz settings should not define
/// a job store "dataSource" to avoid meaningless double configuration.
/// </p>
/// <p>
/// A Spring-specific subclass of Quartz' JobStoreSupport will be used.
/// It is therefore strongly recommended to perform all operations on
/// the Scheduler within Spring-managed transactions.
/// Else, database locking will not properly work and might even break
/// (e.g. if trying to obtain a lock on Oracle without a transaction).
/// </p>
/// </remarks>
/// <seealso cref="QuartzProperties" />
/// <seealso cref="LocalDataSourceJobStore" />
public IDbProvider DbProvider
{
set { dbProvider = value; }
}
/// <summary>
/// Set the name of the object in the object factory that created this object.
/// </summary>
/// <value>The name of the object in the factory.</value>
/// <remarks>
/// <p>
/// Invoked after population of normal object properties but before an init
/// callback like <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
/// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
/// method or a custom init-method.
/// </p>
/// </remarks>
public string ObjectName
{
set
{
if (schedulerName == null)
{
schedulerName = value;
}
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="SchedulerFactoryObject"/> is running.
/// </summary>
/// <value><c>true</c> if running; otherwise, <c>false</c>.</value>
public virtual bool Running
{
get
{
if (scheduler != null)
{
try
{
return !scheduler.InStandbyMode;
}
catch (SchedulerException)
{
return false;
}
}
return false;
}
}
#region IApplicationContextAware Members
/// <summary>
/// Sets the <see cref="Spring.Context.IApplicationContext"/> that this
/// object runs in.
/// </summary>
/// <value></value>
/// <remarks>
/// <p>
/// Normally this call will be used to initialize the object.
/// </p>
/// <p>
/// Invoked after population of normal object properties but before an
/// init callback such as
/// <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
/// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
/// or a custom init-method. Invoked after the setting of any
/// <see cref="Spring.Context.IResourceLoaderAware"/>'s
/// <see cref="Spring.Context.IResourceLoaderAware.ResourceLoader"/>
/// property.
/// </p>
/// </remarks>
/// <exception cref="Spring.Context.ApplicationContextException">
/// In the case of application context initialization errors.
/// </exception>
/// <exception cref="Spring.Objects.ObjectsException">
/// If thrown by any application context methods.
/// </exception>
/// <exception cref="Spring.Objects.Factory.ObjectInitializationException"/>
public virtual IApplicationContext ApplicationContext
{
set { applicationContext = value; }
}
#endregion
#region IDisposable Members
/// <summary>
/// Shut down the Quartz scheduler on object factory Shutdown,
/// stopping all scheduled jobs.
/// </summary>
public virtual void Dispose()
{
logger.Info("Shutting down Quartz Scheduler");
scheduler.Shutdown(waitForJobsToCompleteOnShutdown);
}
#endregion
/// <summary>
/// Template method that determines the Scheduler to operate on.
/// To be implemented by subclasses.
/// </summary>
/// <returns></returns>
protected override IScheduler GetScheduler()
{
return scheduler;
}
#region IFactoryObject Members
/// <summary>
/// Return an instance (possibly shared or independent) of the object
/// managed by this factory.
/// </summary>
/// <returns>
/// An instance (possibly shared or independent) of the object managed by
/// this factory.
/// </returns>
/// <remarks>
/// <note type="caution">
/// If this method is being called in the context of an enclosing IoC container and
/// returns <see langword="null"/>, the IoC container will consider this factory
/// object as not being fully initialized and throw a corresponding (and most
/// probably fatal) exception.
/// </note>
/// </remarks>
public virtual object GetObject()
{
return scheduler;
}
/// <summary>
/// Return the <see cref="System.Type"/> of object that this
/// <see cref="Spring.Objects.Factory.IFactoryObject"/> creates, or
/// <see langword="null"/> if not known in advance.
/// </summary>
/// <value></value>
public virtual Type ObjectType
{
get { return (scheduler != null) ? scheduler.GetType() : typeof (IScheduler); }
}
/// <summary>
/// Is the object managed by this factory a singleton or a prototype?
/// </summary>
/// <value></value>
public virtual bool IsSingleton
{
get { return true; }
}
#endregion
//---------------------------------------------------------------------
// Implementation of IInitializingObject interface
//---------------------------------------------------------------------
#region IInitializingObject Members
/// <summary>
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has injected all of an object's dependencies.
/// </summary>
/// <remarks>
/// <p>
/// This method allows the object instance to perform the kind of
/// initialization only possible when all of it's dependencies have
/// been injected (set), and to throw an appropriate exception in the
/// event of misconfiguration.
/// </p>
/// <p>
/// Please do consult the class level documentation for the
/// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
/// description of exactly <i>when</i> this method is invoked. In
/// particular, it is worth noting that the
/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
/// and <see cref="Spring.Context.IApplicationContextAware"/>
/// callbacks will have been invoked <i>prior</i> to this method being
/// called.
/// </p>
/// </remarks>
/// <exception cref="System.Exception">
/// In the event of misconfiguration (such as the failure to set a
/// required property) or if initialization fails.
/// </exception>
public virtual void AfterPropertiesSet()
{
// Create SchedulerFactory instance.
#if QUARTZ_2_0
ISchedulerFactory schedulerFactory = ObjectUtils.InstantiateType<ISchedulerFactory>(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
/// <summary>
/// Load and/or apply Quartz properties to the given SchedulerFactory.
/// </summary>
/// <param name="schedulerFactory">the SchedulerFactory to Initialize</param>
private void InitSchedulerFactory(ISchedulerFactory schedulerFactory)
{
if (!(schedulerFactory is StdSchedulerFactory))
{
if (configLocation != null || quartzProperties != null || schedulerName != null ||
taskExecutor != null || dbProvider != null)
{
throw new ArgumentException("StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);
}
// Otherwise assume that no initialization is necessary...
return;
}
NameValueCollection mergedProps = new NameValueCollection();
// Set necessary default properties here, as Quartz will not apply
// its default configuration when explicitly given properties.
if (taskExecutor != null)
{
mergedProps[StdSchedulerFactory.PropertyThreadPoolType] =
typeof (LocalTaskExecutorThreadPool).AssemblyQualifiedName;
}
else
{
mergedProps.Set(StdSchedulerFactory.PropertyThreadPoolType, typeof(SimpleThreadPool).AssemblyQualifiedName);
mergedProps[PROP_THREAD_COUNT] = Convert.ToString(DEFAULT_THREAD_COUNT);
}
if (configLocation != null)
{
if (logger.IsInfoEnabled)
{
logger.Info("Loading Quartz config from [" + configLocation + "]");
}
using (StreamReader sr = new StreamReader(configLocation.InputStream))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] lineItems = line.Split(new char[] { '=' }, 2);
if (lineItems.Length == 2)
{
mergedProps[lineItems[0].Trim()] = lineItems[1].Trim();
}
}
}
}
if (quartzProperties != null)
{
// if given quartz properties, merge to them to configuration
MergePropertiesIntoMap(quartzProperties, mergedProps);
}
if (dbProvider != null)
{
mergedProps.Add(StdSchedulerFactory.PropertyJobStoreType, typeof(LocalDataSourceJobStore).AssemblyQualifiedName);
}
// Make sure to set the scheduler name as configured in the Spring configuration.
if (schedulerName != null)
{
mergedProps.Add(StdSchedulerFactory.PropertySchedulerInstanceName, schedulerName);
}
((StdSchedulerFactory) schedulerFactory).Initialize(mergedProps);
}
/// <summary>
/// Merges the properties into map. This effectively also
/// overwrites existing properties with same key in map.
/// </summary>
/// <param name="properties">The properties to merge into given map.</param>
/// <param name="map">The map to merge to.</param>
protected virtual void MergePropertiesIntoMap(IDictionary properties, NameValueCollection map)
{
foreach (string key in properties.Keys)
{
map[key] = (string) properties[key];
}
}
/// <summary>
/// Create the Scheduler instance for the given factory and scheduler name.
/// Called by afterPropertiesSet.
/// </summary>
/// <remarks>
/// Default implementation invokes SchedulerFactory's <code>GetScheduler</code>
/// method. Can be overridden for custom Scheduler creation.
/// </remarks>
/// <param name="schedulerFactory">the factory to create the Scheduler with</param>
/// <param name="schedName">the name of the scheduler to create</param>
/// <returns>the Scheduler instance</returns>
/// <seealso cref="AfterPropertiesSet"/>
/// <seealso cref="ISchedulerFactory.GetScheduler()"/>
protected virtual IScheduler CreateScheduler(ISchedulerFactory schedulerFactory, string schedName)
{
SchedulerRepository repository = SchedulerRepository.Instance;
lock (repository)
{
IScheduler existingScheduler = (schedulerName != null ? repository.Lookup(schedulerName) : null);
IScheduler newScheduler = schedulerFactory.GetScheduler();
if (newScheduler == existingScheduler) {
throw new InvalidOperationException(
string.Format(
"Active Scheduler of name '{0}' already registered in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!",
schedulerName));
}
if (!exposeSchedulerInRepository) {
// Need to explicitly remove it if not intended for exposure,
// since Quartz shares the Scheduler instance by default!
SchedulerRepository.Instance.Remove(newScheduler.SchedulerName);
}
return newScheduler;
}
}
/// <summary>
/// Expose the specified context attributes and/or the current
/// IApplicationContext in the Quartz SchedulerContext.
/// </summary>
private void PopulateSchedulerContext()
{
// Put specified objects into Scheduler context.
if (schedulerContextMap != null)
{
#if QUARTZ_2_0
var dictionary = schedulerContextMap.Cast<DictionaryEntry>().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);
}
}
/// <summary>
/// Start the Quartz Scheduler, respecting the "startDelay" setting.
/// </summary>
/// <param name="sched">the Scheduler to start</param>
/// <param name="startDelay">the time span to wait before starting
/// the Scheduler asynchronously</param>
protected virtual void StartScheduler(IScheduler sched, TimeSpan startDelay)
{
if (startDelay.TotalSeconds <= 0)
{
logger.Info("Starting Quartz Scheduler now");
sched.Start();
}
else
{
if (logger.IsInfoEnabled)
{
logger.Info(
string.Format("Will start Quartz Scheduler [{0}] in {1} seconds", sched.SchedulerName,
startDelay));
}
sched.StartDelayed(startDelay);
}
}
//---------------------------------------------------------------------
// Implementation of Lifecycle interface
//---------------------------------------------------------------------
/// <summary>
/// Starts this instance.
/// </summary>
public virtual void Start()
{
if (scheduler != null)
{
try
{
scheduler.Start();
}
catch (SchedulerException ex)
{
throw new SchedulingException("Could not start Quartz Scheduler", ex);
}
}
}
/// <summary>
/// Stops this instance.
/// </summary>
public virtual void Stop()
{
if (scheduler != null)
{
try
{
scheduler.Standby();
}
catch (SchedulerException ex)
{
throw new SchedulingException("Could not stop Quartz Scheduler", ex);
}
}
}
/// <summary>
/// Handles the application context's refresh event and starts the scheduler.
/// </summary>
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);
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
namespace Spring.Scheduling.Quartz
{
/// <summary>
/// Generic scheduling exception.
/// </summary>
public class SchedulingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="SchedulingException"/> class.
/// </summary>
/// <param name="message">The message.</param>
public SchedulingException(string message) : base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SchedulingException"/> class.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="ex">The original exception.</param>
public SchedulingException(string message, Exception ex) : base(message, ex)
{
}
}
}

View File

@@ -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
{
/// <summary>
/// Subclass of Quartz's SimpleThreadPool that implements Spring's
/// TaskExecutor interface and listens to Spring lifecycle callbacks.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <seealso cref="SimpleThreadPool" />
/// <seealso cref="ITaskExecutor" />
/// <seealso cref="SchedulerFactoryObject.TaskExecutor" />
public class SimpleThreadPoolTaskExecutor : SimpleThreadPool, ISchedulingTaskExecutor, IInitializingObject, IDisposable
{
private bool waitForJobsToCompleteOnShutdown = false;
/// <summary>
/// Set whether to wait for running jobs to complete on Shutdown.
/// Default is "false".
/// </summary>
/// <value>
/// <c>true</c> if [wait for jobs to complete on shutdown]; otherwise, <c>false</c>.
/// </value>
/// <seealso cref="SimpleThreadPool.Shutdown(bool)"/>
public virtual bool WaitForJobsToCompleteOnShutdown
{
set { waitForJobsToCompleteOnShutdown = value; }
}
/// <summary>
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has injected all of an object's dependencies.
/// </summary>
/// <remarks>
/// <p>
/// This method allows the object instance to perform the kind of
/// initialization only possible when all of it's dependencies have
/// been injected (set), and to throw an appropriate exception in the
/// event of misconfiguration.
/// </p>
/// <p>
/// Please do consult the class level documentation for the
/// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
/// description of exactly <i>when</i> this method is invoked. In
/// particular, it is worth noting that the
/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
/// and <see cref="Spring.Context.IApplicationContextAware"/>
/// callbacks will have been invoked <i>prior</i> to this method being
/// called.
/// </p>
/// </remarks>
/// <exception cref="System.Exception">
/// In the event of misconfiguration (such as the failure to set a
/// required property) or if initialization fails.
/// </exception>
public virtual void AfterPropertiesSet()
{
Initialize();
}
/// <summary>
/// Executes the specified task.
/// </summary>
/// <param name="task">The task.</param>
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");
}
}
/// <summary> This task executor prefers short-lived work units.</summary>
public virtual bool PrefersShortLivedTasks
{
get { return true; }
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
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();
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Adapts Spring's <see cref="Data.Common.IDbProvider"/> to Quartz's
/// <see cref="IDbProvider"/>.
/// </summary>
public class SpringDbProviderAdapter : IDbProvider
{
private readonly Data.Common.IDbProvider dbProvider;
private readonly DbMetadata metadata;
/// <summary>
/// Initializes a new instance of the <see cref="SpringDbProviderAdapter"/> class.
/// </summary>
/// <param name="dbProvider">The Spring db provider.</param>
public SpringDbProviderAdapter(Data.Common.IDbProvider dbProvider)
{
this.dbProvider = dbProvider;
metadata = new SpringMetadataAdapter(dbProvider.DbMetadata);
}
/// <summary>
/// Creates the command.
/// </summary>
/// <returns></returns>
public IDbCommand CreateCommand()
{
return dbProvider.CreateCommand();
}
/// <summary>
/// Creates the command builder.
/// </summary>
/// <returns></returns>
public object CreateCommandBuilder()
{
return dbProvider.CreateCommandBuilder();
}
/// <summary>
/// Creates the connection.
/// </summary>
/// <returns></returns>
public IDbConnection CreateConnection()
{
return dbProvider.CreateConnection();
}
/// <summary>
/// Creates the parameter.
/// </summary>
/// <returns></returns>
public IDbDataParameter CreateParameter()
{
return dbProvider.CreateParameter();
}
/// <summary>
/// Shutdowns this instance.
/// </summary>
public void Shutdown()
{
// no-op
}
/// <summary>
/// Gets or sets the connection string.
/// </summary>
/// <value>The connection string.</value>
public string ConnectionString
{
get { return dbProvider.ConnectionString; }
set { dbProvider.ConnectionString = value; }
}
/// <summary>
/// Gets the metadata.
/// </summary>
/// <value>The metadata.</value>
public DbMetadata Metadata
{
get { return metadata; }
}
}
/// <summary>
/// Helper class to map between Quartz and Spring DB metadata.
/// </summary>
public class SpringMetadataAdapter : DbMetadata
{
private readonly IDbMetadata metadata;
private readonly Enum dbTypeBinary;
/// <summary>
/// Initializes a new instance of the <see cref="SpringMetadataAdapter"/> class.
/// </summary>
/// <param name="metadata">The metadata to wrap and adapt.</param>
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;
}
}
/// <summary>
/// Gets or sets the name of the product.
/// </summary>
/// <value>The name of the product.</value>
public override string ProductName
{
get { return metadata.ProductName; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the type of the connection.
/// </summary>
/// <value>The type of the connection.</value>
public override Type ConnectionType
{
get { return metadata.ConnectionType; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the type of the command.
/// </summary>
/// <value>The type of the command.</value>
public override Type CommandType
{
get { return metadata.CommandType; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the type of the parameter.
/// </summary>
/// <value>The type of the parameter.</value>
public override Type ParameterType
{
get { return metadata.ParameterType; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the type of the command builder.
/// </summary>
/// <value>The type of the command builder.</value>
public override Type CommandBuilderType
{
get { return metadata.CommandBuilderType; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the command builder derive parameters method.
/// </summary>
/// <value>The command builder derive parameters method.</value>
public override MethodInfo CommandBuilderDeriveParametersMethod
{
get { return metadata.CommandBuilderDeriveParametersMethod; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the parameter name prefix.
/// </summary>
/// <value>The parameter name prefix.</value>
public override string ParameterNamePrefix
{
get { return metadata.ParameterNamePrefix; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the type of the exception.
/// </summary>
/// <value>The type of the exception.</value>
public override Type ExceptionType
{
get { return metadata.ExceptionType; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets a value indicating whether [bind by name].
/// </summary>
/// <value><c>true</c> if [bind by name]; otherwise, <c>false</c>.</value>
public override bool BindByName
{
get { return metadata.BindByName; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the type of the parameter db.
/// </summary>
/// <value>The type of the parameter db.</value>
public override Type ParameterDbType
{
get { return metadata.ParameterDbType; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the parameter db type property.
/// </summary>
/// <value>The parameter db type property.</value>
public override PropertyInfo ParameterDbTypeProperty
{
get { return metadata.ParameterDbTypeProperty; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets or sets the parameter is nullable property.
/// </summary>
/// <value>The parameter is nullable property.</value>
public override PropertyInfo ParameterIsNullableProperty
{
get { return metadata.ParameterIsNullableProperty; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Gets the type of the db binary.
/// </summary>
/// <value>The type of the db binary.</value>
public override Enum DbBinaryType
{
get { return dbTypeBinary; }
}
/// <summary>
/// Gets or sets a value indicating whether [use parameter name prefix in parameter collection].
/// </summary>
/// <value>
/// <c>true</c> if [use parameter name prefix in parameter collection]; otherwise, <c>false</c>.
/// </value>
public override bool UseParameterNamePrefixInParameterCollection
{
get { return metadata.UseParameterNamePrefixInParameterCollection; }
set { throw new NotSupportedException(); }
}
}
}

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <seealso cref="SchedulerFactoryObject.JobFactory" />
/// <seealso cref="QuartzJobObject" />
public class SpringObjectJobFactory : AdaptableJobFactory, ISchedulerContextAware
{
private string[] ignoredUnknownProperties;
private SchedulerContext schedulerContext;
/// <summary>
/// Specify the unknown properties (not found in the object) that should be ignored.
/// </summary>
/// <remarks>
/// Default is <code>null</code>, 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).
/// </remarks>
public virtual string[] IgnoredUnknownProperties
{
set { ignoredUnknownProperties = value; }
}
/// <summary>
/// Set the SchedulerContext of the current Quartz Scheduler.
/// </summary>
/// <value></value>
/// <seealso cref="IScheduler.Context"/>
public virtual SchedulerContext SchedulerContext
{
set { schedulerContext = value; }
}
/// <summary>
/// Create the job instance, populating it with property values taken
/// from the scheduler context, job data map and trigger data map.
/// </summary>
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;
}
/// <summary>
/// Return whether the given job object is eligible for having
/// its object properties populated.
/// <p>
/// The default implementation ignores QuartzJobObject instances,
/// which will inject object properties themselves.
/// </p>
/// </summary>
/// <param name="jobObject">
/// The job object to introspect.
/// </param>
/// <seealso cref="QuartzJobObject" />
protected virtual bool IsEligibleForPropertyPopulation(object jobObject)
{
return (!(jobObject is QuartzJobObject));
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace Spring.Scheduling
{
/// <summary>
/// Summary description for TaskRejectedException.
/// </summary>
public class TaskRejectedException : ApplicationException
{
}
}

View File

@@ -51,69 +51,31 @@
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Spring.Scheduling.Quartz\JobMethodInvocationFailedException.cs">
<Link>JobMethodInvocationFailedException.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\AdaptableJobFactory.cs">
<Link>Scheduling\Quartz\AdaptableJobFactory.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\DelegatingJob.cs">
<Link>Scheduling\Quartz\DelegatingJob.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\IJobDetailAwareTrigger.cs">
<Link>Scheduling\Quartz\IJobDetailAwareTrigger.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\ISchedulerContextAware.cs">
<Link>Scheduling\Quartz\ISchedulerContextAware.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\ISchedulingTaskExecutor.cs">
<Link>Scheduling\Quartz\ISchedulingTaskExecutor.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\ITaskExecutor.cs">
<Link>Scheduling\Quartz\ITaskExecutor.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\LocalDataSourceJobStore.cs">
<Link>Scheduling\Quartz\LocalDataSourceJobStore.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\LocalTaskExecutorThreadPool.cs">
<Link>Scheduling\Quartz\LocalTaskExecutorThreadPool.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\MethodInvokingJob.cs">
<Link>Scheduling\Quartz\MethodInvokingJob.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\MethodInvokingRunnable.cs">
<Link>Scheduling\Quartz\MethodInvokingRunnable.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\QuartzJobObject.cs">
<Link>Scheduling\Quartz\QuartzJobObject.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\SchedulerAccessorObject.cs">
<Link>Scheduling\Quartz\SchedulerAccessorObject.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\SchedulerFactoryObject.cs">
<Link>Scheduling\Quartz\SchedulerFactoryObject.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\SchedulingException.cs">
<Link>Scheduling\Quartz\SchedulingException.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\SimpleThreadPoolTaskExecutor.cs">
<Link>Scheduling\Quartz\SimpleThreadPoolTaskExecutor.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\SpringDbProviderAdapter.cs">
<Link>Scheduling\Quartz\SpringDbProviderAdapter.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\SpringObjectJobFactory.cs">
<Link>Scheduling\Quartz\SpringObjectJobFactory.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz\Scheduling\Quartz\TaskRejectedException.cs">
<Link>Scheduling\Quartz\TaskRejectedException.cs</Link>
</Compile>
<Compile Include="JobMethodInvocationFailedException.cs" />
<Compile Include="Scheduling\Quartz\AdaptableJobFactory.cs" />
<Compile Include="Scheduling\Quartz\CronTriggerObject.cs" />
<Compile Include="Scheduling\Quartz\DelegatingJob.cs" />
<Compile Include="Scheduling\Quartz\IJobDetailAwareTrigger.cs" />
<Compile Include="Scheduling\Quartz\ISchedulerContextAware.cs" />
<Compile Include="Scheduling\Quartz\ISchedulingTaskExecutor.cs" />
<Compile Include="Scheduling\Quartz\ITaskExecutor.cs" />
<Compile Include="Scheduling\Quartz\JobDetailObject.cs" />
<Compile Include="Scheduling\Quartz\LocalDataSourceJobStore.cs" />
<Compile Include="Scheduling\Quartz\LocalTaskExecutorThreadPool.cs" />
<Compile Include="Scheduling\Quartz\MethodInvokingJob.cs" />
<Compile Include="Scheduling\Quartz\MethodInvokingJobDetailFactoryObject.cs" />
<Compile Include="Scheduling\Quartz\MethodInvokingRunnable.cs" />
<Compile Include="Scheduling\Quartz\QuartzJobObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulerAccessor.cs" />
<Compile Include="Scheduling\Quartz\SchedulerAccessorObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulerFactoryObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulingException.cs" />
<Compile Include="Scheduling\Quartz\SimpleThreadPoolTaskExecutor.cs" />
<Compile Include="Scheduling\Quartz\SimpleTriggerObject.cs" />
<Compile Include="Scheduling\Quartz\SpringDbProviderAdapter.cs" />
<Compile Include="Scheduling\Quartz\SpringObjectJobFactory.cs" />
<Compile Include="Scheduling\Quartz\StatefulMethodInvokingJob.cs" />
<Compile Include="Scheduling\Quartz\TaskRejectedException.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spring.Core\Spring.Core.2010.csproj">

View File

@@ -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
{
/// <summary>Test that the assembly is built with the correct DebugAttributes in release and debug builds.
/// </summary>
/// <author>Mark Pollack</author>
[TestFixture]
public sealed class QuartzCompilerOptionTests : CompilerOptionsTests
{
[TestFixtureSetUp]
public void FixtureSetUp()
{
AssemblyToCheck = Assembly.GetAssembly(typeof (SchedulingException));
}
}
}

View File

@@ -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
{
/// <summary>
/// Tests for <see cref="AdaptableJobFactory" />.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class AdaptableJobFactoryTest
{
private AdaptableJobFactory jobFactory;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
jobFactory = new AdaptableJobFactory();
}
/// <summary>
/// Tests job creation.
/// </summary>
[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");
}
}
/// <summary>
/// Tests job creation.
/// </summary>
[Test]
public void TestNewJob_ThreadStartJob()
{
// TODO ThreadStart is not the way to go
}
/// <summary>
/// Tests job creation.
/// </summary>
[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);
}
}
}

View File

@@ -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
{
/// <summary>
/// Tests for <see cref="CronTriggerObject" />.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class CronTriggerObjectTest : TriggerObjectTest
{
private CronTriggerObject cronTrigger;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
cronTrigger = new CronTriggerObject();
cronTrigger.ObjectName = TRIGGER_NAME;
Trigger = cronTrigger;
}
/// <summary>
/// Tests all possible misfire instructions for cron trigger
/// from strings to int.
/// </summary>
[Test]
public void TestMisfireInstructionNames()
{
string[] names = new string[] { "DoNothing", "FireOnceNow", "SmartPolicy" };
foreach (string name in names)
{
cronTrigger.MisfireInstructionName = name;
}
}
/// <summary>
/// Tests that JobDetail defaults values as expected in AfterPropertiesSet.
/// </summary>
[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");
}
/// <summary>
/// Tests that JobDetail maps job data map as expected.
/// </summary>
[Test]
public void TestJobDataAsMap()
{
#if QUARTZ_2_0
IDictionary data = new Dictionary<string, object>();
#else
Hashtable data = new Hashtable();
#endif
data["foo"] = "bar";
data["number"] = 123;
cronTrigger.JobDataAsMap = data;
CollectionAssert.AreEquivalent(data, cronTrigger.JobDataMap, "Data differed");
}
/// <summary>
/// Tests that StartDelay is respected.
/// </summary>
[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)));
}
}
}

View File

@@ -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
{
/// <summary>
/// Tests for <see cref="JobDetailObject" />.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class JobDetailObjectTest
{
private JobDetailObject jobDetail;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
jobDetail = new JobDetailObject();
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestJobType_Null()
{
jobDetail.JobType = null;
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[Test]
public void TestJobType_NonIJob()
{
jobDetail.JobType = typeof(object);
Assert.AreEqual(typeof(object), jobDetail.JobType, "JobDetail did not create same type as expected");
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[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");
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestJobDataAsMap_Null()
{
jobDetail.JobDataAsMap = null;
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[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");
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[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");
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[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");
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[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");
}
/// <summary>
/// Tests job detail's property behavior.
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestAfterPropertiesSet_ApplicationContextJobDataKeySetWithoutApplicationContext()
{
const string objectName = "springJobDetailObject";
jobDetail.ObjectName = objectName;
jobDetail.ApplicationContextJobDataKey = "applicationContextJobDataKey";
jobDetail.AfterPropertiesSet();
}
}
}

View File

@@ -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
{
/// <summary>
/// Unit tests for MethodInvokingJobDetailFactoryObject.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class MethodInvokingJobDetailFactoryObjectTest
{
private const string FACTORY_NAME = "springObjectFactory";
private MethodInvokingJobDetailFactoryObject factory;
/// <summary>
/// Setup for the test.
/// </summary>
[SetUp]
public void SetUp()
{
factory = new MethodInvokingJobDetailFactoryObject();
factory.ObjectName = FACTORY_NAME;
factory.TargetMethod = "Invoke";
factory.TargetObject = new InvocationCountingJob();
}
/// <summary>
/// Tests JobDetail retrieval and it's set properties.
/// </summary>
[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
}
/// <summary>
/// Tests JobDetail retrieval and it's set properties.
/// </summary>
[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
/// <summary>
/// Tests JobDetail retrieval and it's set properties.
/// </summary>
[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
}
}

View File

@@ -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
{
/// <summary>
/// Tests for MethodInvokingJob.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class MethodInvokingJobTest
{
private MethodInvokingJob methodInvokingJob;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
methodInvokingJob = new MethodInvokingJob();
}
/// <summary>
/// Test method invoke via execute.
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestMethodInvoker_SetWithNull()
{
methodInvokingJob.MethodInvoker = null;
}
/// <summary>
/// Test method invoke via execute.
/// </summary>
[Test]
[ExpectedException(typeof(JobExecutionException))]
public void TestMethodInvocation_NullMethodInvokder()
{
methodInvokingJob.Execute(CreateMinimalJobExecutionContext());
}
/// <summary>
/// Test method invoke via execute.
/// </summary>
[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");
}
/// <summary>
/// Test that invocation result is set to execution context (SPRNET-1340).
/// </summary>
[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");
}
/// <summary>
/// Test method invoke via execute.
/// </summary>
[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");
}
/// <summary>
/// Test method invoke via execute.
/// </summary>
[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;
}
}
/// <summary>
/// Test class for method invoker.
/// </summary>
public class InvocationCountingJob
{
private int counter;
internal const string DefaultReturnValue = "return value";
/// <summary>
/// Increments method invoke counter.
/// </summary>
public void Invoke()
{
Interlocked.Increment(ref counter);
}
/// <summary>
/// Throws exception after incrementing counter.
/// </summary>
public void InvokeAndThrowException()
{
Interlocked.Increment(ref counter);
throw new Exception();
}
private void PrivateMethod()
{
}
/// <summary>
/// Returns <see cref="DefaultReturnValue" /> as return value.
/// </summary>
public string InvokeWithReturnValue()
{
return DefaultReturnValue;
}
/// <summary>
/// Invocation count.
/// </summary>
public int CounterValue
{
get { return counter; }
}
}
}

View File

@@ -0,0 +1,45 @@
namespace Spring.Scheduling.Quartz
{
/// <summary>
/// A simple test object for Quartz.NET to run
/// that simulates imports and exports.
/// </summary>
/// <author>Rob Harrop</author>
public class QuartzTestObject
{
private int exportCount;
private int importCount;
/// <summary>
/// Executes a fake import and increments counter.
/// </summary>
public void DoImport()
{
++importCount;
}
///<summary>
/// Executes a fake export and increments counter.
///</summary>
public void DoExport()
{
++exportCount;
}
/// <summary>
/// Tells how many times import has been done.
/// </summary>
public int ImportCount
{
get { return importCount; }
}
/// <summary>
/// Tells how many times export has been done.
/// </summary>
public int ExportCount
{
get { return exportCount; }
}
}
}

View File

@@ -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
{
/// <summary>
/// Tests for SchedulerFactoryObject.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class SchedulerFactoryObjectTest
{
private static readonly MethodInfo m_InitSchedulerFactory = typeof(SchedulerFactoryObject).GetMethod("InitSchedulerFactory",
BindingFlags.Instance | BindingFlags.NonPublic);
private SchedulerFactoryObject factory;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
factory = new SchedulerFactoryObject();
TestSchedulerFactory.Initialize();
TestSchedulerFactory.MockScheduler.Stub(x => x.SchedulerName).Return("scheduler").Repeat.Any();
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestAfterPropertiesSet_Defaults()
{
factory.AfterPropertiesSet();
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestAfterPropertiesSet_NullJobFactory()
{
factory.JobFactory = null;
factory.AfterPropertiesSet();
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[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
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestAfterPropertiesSet_AddListeners()
{
InitForAfterPropertiesSetTest();
factory.SchedulerListeners = new ISchedulerListener[] { MockRepository.GenerateMock<ISchedulerListener>() };
factory.GlobalJobListeners = new IJobListener[] { MockRepository.GenerateMock<IJobListener>() };
factory.JobListeners = new IJobListener[] { MockRepository.GenerateMock<IJobListener>() };
factory.GlobalTriggerListeners = new ITriggerListener[] { MockRepository.GenerateMock<ITriggerListener>() };
factory.TriggerListeners = new ITriggerListener[] { MockRepository.GenerateMock<ITriggerListener>() };
factory.AfterPropertiesSet();
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddSchedulerListener(Arg<ISchedulerListener>.Is.NotNull));
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddGlobalJobListener(Arg<IJobListener>.Is.NotNull));
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddJobListener(Arg<IJobListener>.Is.NotNull));
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddGlobalTriggerListener(Arg<ITriggerListener>.Is.NotNull));
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddTriggerListener(Arg<ITriggerListener>.Is.NotNull));
}
#endif
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestAfterPropertiesSet_Calendars()
{
InitForAfterPropertiesSetTest();
const string calendarName = "calendar";
ICalendar cal = MockRepository.GenerateMock<ICalendar>();
Hashtable calTable = new Hashtable();
calTable[calendarName] = cal;
factory.Calendars = calTable;
factory.AfterPropertiesSet();
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.AddCalendar(calendarName, cal, true, true));
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[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();
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[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;
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestStart()
{
factory.SchedulerFactoryType = typeof(TestSchedulerFactory);
factory.AutoStartup = false;
factory.AfterPropertiesSet();
factory.Start();
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.JobFactory = Arg<IJobFactory>.Is.NotNull);
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.Start());
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestStop()
{
factory.SchedulerFactoryType = typeof(TestSchedulerFactory);
factory.AutoStartup = false;
factory.AfterPropertiesSet();
factory.Stop();
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.JobFactory = Arg<IJobFactory>.Is.NotNull);
TestSchedulerFactory.MockScheduler.AssertWasCalled(x => x.Standby());
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestGetObject()
{
factory.AfterPropertiesSet();
IScheduler sched = (IScheduler)factory.GetObject();
Assert.IsNotNull(sched, "scheduler was null");
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestSchedulerFactoryType_InvalidType()
{
factory.SchedulerFactoryType = typeof(SchedulerFactoryObjectTest);
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestSchedulerFactoryType_ValidType()
{
factory.SchedulerFactoryType = typeof(StdSchedulerFactory);
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestInitSchedulerFactory_MinimalDefaults()
{
factory.SchedulerName = "testFactoryObject";
StdSchedulerFactory factoryToPass = new StdSchedulerFactory();
m_InitSchedulerFactory.Invoke(factory, new object[] { factoryToPass });
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[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)
{
}
}
/// <summary>
/// ISchedulerFactory implementation for testing purposes.
/// </summary>
public class TestSchedulerFactory : ISchedulerFactory
{
private static IScheduler mockScheduler;
/// <summary>
/// The mocked scheduler.
/// </summary>
public static IScheduler MockScheduler
{
get { return mockScheduler; }
}
///<summary>
///</summary>
///<returns></returns>
public IScheduler GetScheduler()
{
return mockScheduler;
}
///<summary>
///</summary>
///<param name="schedName"></param>
///<returns></returns>
public IScheduler GetScheduler(string schedName)
{
return mockScheduler;
}
#if QUARTZ_2_0
///<summary>
///</summary>
public ICollection<IScheduler> AllSchedulers
{
get { return new List<IScheduler>(); }
}
#else
///<summary>
///</summary>
public ICollection AllSchedulers
{
get { return new ArrayList(); }
}
#endif
public static void Initialize()
{
mockScheduler = MockRepository.GenerateMock<IScheduler>();
}
}
///<summary>
/// Scheduler factory that supports property interception.
///</summary>
public class InterceptingStdSChedulerFactory : StdSchedulerFactory
{
private NameValueCollection properties;
///<summary>
/// Initializes the factory.
///</summary>
///<param name="props"></param>
public override void Initialize(NameValueCollection props)
{
this.properties = props;
}
/// <summary>
/// Return propeties given to this factory at initialization time.
/// </summary>
public NameValueCollection Properties
{
get { return properties; }
}
}
}

View File

@@ -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
{
/// <summary>
/// Tests for <see cref="SimpleTriggerObject" />.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class SimpleTriggerObjectTest : TriggerObjectTest
{
private SimpleTriggerObject simpleTrigger;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
simpleTrigger = new SimpleTriggerObject();
simpleTrigger.ObjectName = TRIGGER_NAME;
Trigger = simpleTrigger;
}
/// <summary>
/// Tests all possible misfire instructions for cron trigger
/// from strings to int.
/// </summary>
[Test]
public void TestMisfireInstructionNames()
{
string[] names = new string[] { "FireNow", "RescheduleNextWithExistingCount", "RescheduleNextWithRemainingCount", "RescheduleNowWithExistingRepeatCount", "RescheduleNowWithRemainingRepeatCount", "SmartPolicy" };
foreach (string name in names)
{
simpleTrigger.MisfireInstructionName = name;
}
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public override void TestAfterPropertiesSet_Defaults()
{
simpleTrigger.AfterPropertiesSet();
base.TestAfterPropertiesSet_Defaults();
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public override void TestAfterPropertiesSet_ValuesGiven()
{
simpleTrigger.StartDelay = TimeSpan.FromMilliseconds(100);
simpleTrigger.AfterPropertiesSet();
base.TestAfterPropertiesSet_ValuesGiven();
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[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);
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[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");
}
/// <summary>
/// Tests AfterPropertiesSet behavior.
/// </summary>
[Test]
public void TestJobDataAsMap()
{
#if QUARTZ_2_0
IDictionary data = new Dictionary<string, object>();
#else
Hashtable data = new Hashtable();
#endif
data["foo"] = "bar";
data["number"] = 123;
simpleTrigger.JobDataAsMap = data;
CollectionAssert.AreEquivalent(data, simpleTrigger.JobDataMap, "Data differed");
}
}
}

View File

@@ -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
{
/// <summary>
/// Unit tests for SpringObjectJobFactory.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
[TestFixture]
public class SpringObjectJobFactoryTest
{
private SpringObjectJobFactory factory;
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public void SetUp()
{
factory = new SpringObjectJobFactory();
}
/// <summary>
/// Tests job instane creation.
/// </summary>
[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");
}
/// <summary>
/// Tests job instane creation.
/// </summary>
[Test]
public void TestCreateJobInstance_SchedulerContextGiven()
{
Trigger trigger = new SimpleTrigger();
TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(InjectableJob), trigger);
#if QUARTZ_2_0
IDictionary<string, object> items = new Dictionary<string, object>();
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");
}
/// <summary>
/// Tests job instane creation.
/// </summary>
[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 ");
}
}
/// <summary>
/// Test job object that has injectable properties
/// </summary>
public class InjectableJob : NoOpJob
{
private int number;
private string foo;
/// <summary>
/// Simple int property.
/// </summary>
public int Number
{
get { return number; }
set { number = value; }
}
/// <summary>
/// Simple string property.
/// </summary>
public string Foo
{
get { return foo; }
set { foo = value; }
}
}
}

View File

@@ -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
{
/// <summary>
/// Simple test task.
/// </summary>
/// <author>Juergen Hoeller</author>
public class TestMethodInvokingTask
{
/// <summary>
/// Counter for DoSomething and DoWait calls.
/// </summary>
public int counter;
private readonly object lockObject = new object();
/// <summary>
/// Simple test method.
/// </summary>
public void DoSomething()
{
counter++;
}
/// <summary>
/// Waits until stop is called.
/// </summary>
public void DoWait()
{
counter++;
// wait until stop is called
lock (lockObject)
{
try
{
Monitor.Wait(lockObject);
}
catch (ThreadInterruptedException)
{
// fall through
}
}
}
/// <summary>
/// Informs test object that stop should be called.
/// </summary>
public void Stop()
{
lock (lockObject)
{
Monitor.Pulse(lockObject);
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Quartz.NET integration testing helpers.
/// </summary>
/// <author>Marko Lahma (.NET)</author>
public class TestUtil
{
/// <summary>
/// Creates the minimal fired bundle with job detail that has
/// given job type.
/// </summary>
/// <param name="jobType">Type of the job.</param>
/// <returns>Minimal TriggerFiredBundle</returns>
public static TriggerFiredBundle CreateMinimalFiredBundleWithTypedJobDetail(Type jobType)
{
return CreateMinimalFiredBundleWithTypedJobDetail(jobType, null);
}
/// <summary>
/// Creates the minimal fired bundle with job detail that has
/// given job type.
/// </summary>
/// <param name="jobType">Type of the job.</param>
/// <param name="trigger">The trigger.</param>
/// <returns>Minimal TriggerFiredBundle</returns>
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;
}
}
}

View File

@@ -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
{
/// <summary>
/// Base class for testing triggers. Contains common functionality.
/// </summary>
[TestFixture]
public abstract class TriggerObjectTest
{
private Trigger trigger;
/// <summary>
/// Constant name for tested triggers.
/// </summary>
protected const string TRIGGER_NAME = "trigger";
/// <summary>
/// TriggerObject under test.
/// </summary>
protected Trigger Trigger
{
set { trigger = value; }
}
/// <summary>
/// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
/// </summary>
[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);
}
/// <summary>
/// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
/// </summary>
[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);
}
/// <summary>
/// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
/// </summary>
[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
/// <summary>
/// Tests whether two datetimes are close enough.
/// </summary>
/// <param name="d1"></param>
/// <param name="d2"></param>
/// <param name="allowedDeltaInMilliseconds"></param>
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
/// <summary>
/// Tests whether two datetimes are close enough.
/// </summary>
/// <param name="d1"></param>
/// <param name="d2"></param>
/// <param name="allowedDeltaInMilliseconds"></param>
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");
}
/// <summary>
/// Tests that TriggerObject defaults values as expected in AfterPropertiesSet.
/// </summary>
[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
}
}

View File

@@ -71,50 +71,6 @@
<Name>Spring.Core.Tests.2010</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\QuartzCompilerOptionsTests.cs">
<Link>QuartzCompilerOptionsTests.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\AdaptableJobFactoryTest.cs">
<Link>Scheduling\Quartz\AdaptableJobFactoryTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\CronTriggerObjectTest.cs">
<Link>Scheduling\Quartz\CronTriggerObjectTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\JobDetailObjectTest.cs">
<Link>Scheduling\Quartz\JobDetailObjectTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\MethodInvokingJobDetailFactoryObjectTest.cs">
<Link>Scheduling\Quartz\MethodInvokingJobDetailFactoryObjectTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\MethodInvokingJobTest.cs">
<Link>Scheduling\Quartz\MethodInvokingJobTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\QuartzSupportTests.cs">
<Link>Scheduling\Quartz\QuartzSupportTests.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\QuartzTestObject.cs">
<Link>Scheduling\Quartz\QuartzTestObject.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\SchedulerFactoryObjectTest.cs">
<Link>Scheduling\Quartz\SchedulerFactoryObjectTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\SimpleTriggerObjectTest.cs">
<Link>Scheduling\Quartz\SimpleTriggerObjectTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\SpringObjectJobFactoryTest.cs">
<Link>Scheduling\Quartz\SpringObjectJobFactoryTest.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\TestMethodInvokingTask.cs">
<Link>Scheduling\Quartz\TestMethodInvokingTask.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\TestUtil.cs">
<Link>Scheduling\Quartz\TestUtil.cs</Link>
</Compile>
<Compile Include="..\Spring.Scheduling.Quartz.Tests\Scheduling\Quartz\TriggerObjectTest.cs">
<Link>Scheduling\Quartz\TriggerObjectTest.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Content Include="job-scheduling-data.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -137,6 +93,23 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Compile Include="QuartzCompilerOptionsTests.cs" />
<Compile Include="Scheduling\Quartz\AdaptableJobFactoryTest.cs" />
<Compile Include="Scheduling\Quartz\CronTriggerObjectTest.cs" />
<Compile Include="Scheduling\Quartz\JobDetailObjectTest.cs" />
<Compile Include="Scheduling\Quartz\MethodInvokingJobDetailFactoryObjectTest.cs" />
<Compile Include="Scheduling\Quartz\MethodInvokingJobTest.cs" />
<Compile Include="Scheduling\Quartz\QuartzSupportTests.cs" />
<Compile Include="Scheduling\Quartz\QuartzTestObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulerFactoryObjectTest.cs" />
<Compile Include="Scheduling\Quartz\SimpleTriggerObjectTest.cs" />
<Compile Include="Scheduling\Quartz\SpringObjectJobFactoryTest.cs" />
<Compile Include="Scheduling\Quartz\TestMethodInvokingTask.cs" />
<Compile Include="Scheduling\Quartz\TestUtil.cs" />
<Compile Include="Scheduling\Quartz\TriggerObjectTest.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.