Brought more tests from Java side and synchronized with Java code base:

use object name as default scheduler name (when "SchedulerName" property not explicitly specified); do not accept a pre-registered Scheduler instance in the Quartz SchedulerRepository anymore; do not expose the Spring-created/managed Scheduler to the Quartz SchedulerRepository anymore; added "ExposeSchedulerInRepository" flag for explicit exposure to the SchedulerRepository; introduced SchedulerAccessorObject for registering jobs/triggers/listeners on an existing Quartz Scheduler instance
This commit is contained in:
lahma
2008-11-06 18:08:46 +00:00
parent a9678d17f5
commit ebe585de68
21 changed files with 2277 additions and 455 deletions

View File

@@ -57,6 +57,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5ECE7B2B
Release.AspNetCompiler.Debug = "False"
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.Tests.2005", "..\..\test\Spring\Spring.Core.Tests\Spring.Core.Tests.2005.csproj", "{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}"
ProjectSection(WebsiteProperties) = preProject
Debug.AspNetCompiler.Debug = "True"
Release.AspNetCompiler.Debug = "False"
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|.NET = Debug|.NET
@@ -133,17 +139,28 @@ Global
{E4328F68-C3FC-43E2-9D63-30CEC55215B0}.Release|Any CPU.Build.0 = Release|Any CPU
{E4328F68-C3FC-43E2-9D63-30CEC55215B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E4328F68-C3FC-43E2-9D63-30CEC55215B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|.NET.ActiveCfg = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|.NET.ActiveCfg = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.Build.0 = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3A3A4E65-45A6-4B20-B460-0BEDC302C02C} = {438FE108-6F55-4C8A-8BF4-C85D0906D134}
{710961A3-0DF4-49E4-A26E-F5B9C044AC84} = {438FE108-6F55-4C8A-8BF4-C85D0906D134}
{AE00E5AB-C39A-436F-86D2-33BFE33E2E40} = {438FE108-6F55-4C8A-8BF4-C85D0906D134}
{E823D54C-CE82-4868-929F-5F95A999F61E} = {438FE108-6F55-4C8A-8BF4-C85D0906D134}
{9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5} = {5ECE7B2B-6975-4800-982E-618794909499}
{710961A3-0DF4-49E4-A26E-F5B9C044AC84} = {438FE108-6F55-4C8A-8BF4-C85D0906D134}
{E4328F68-C3FC-43E2-9D63-30CEC55215B0} = {5ECE7B2B-6975-4800-982E-618794909499}
{9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5} = {5ECE7B2B-6975-4800-982E-618794909499}
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904} = {5ECE7B2B-6975-4800-982E-618794909499}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
NAntAddinLastFileName = Spring.build

View File

@@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Scheduling.Quartz.Te
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Scheduling.Quartz.Integration.Tests.2008", "..\..\test\Spring\Spring.Scheduling.Quartz.Integration.Tests\Spring.Scheduling.Quartz.Integration.Tests.2008.csproj", "{E4328F68-C3FC-43E2-9D63-30CEC55215B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.Tests.2008", "..\..\test\Spring\Spring.Core.Tests\Spring.Core.Tests.2008.csproj", "{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|.NET = Debug|.NET
@@ -97,6 +99,16 @@ Global
{E4328F68-C3FC-43E2-9D63-30CEC55215B0}.Release|Any CPU.Build.0 = Release|Any CPU
{E4328F68-C3FC-43E2-9D63-30CEC55215B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E4328F68-C3FC-43E2-9D63-30CEC55215B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|.NET.ActiveCfg = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|.NET.ActiveCfg = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.Build.0 = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -108,6 +120,7 @@ Global
{E823D54C-CE82-4868-929F-5F95A999F61E} = {438FE108-6F55-4C8A-8BF4-C85D0906D134}
{9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5} = {5ECE7B2B-6975-4800-982E-618794909499}
{E4328F68-C3FC-43E2-9D63-30CEC55215B0} = {5ECE7B2B-6975-4800-982E-618794909499}
{44B16BAA-6DF8-447C-9D7F-3AD3D854D904} = {5ECE7B2B-6975-4800-982E-618794909499}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
NAntAddinLastFileName = Spring.build

View File

@@ -100,6 +100,10 @@ namespace Spring.Scheduling.Quartz
{
return new DelegatingJob((ThreadStart)jobObject);
}
else if (jobObject is IThreadRunnable)
{
return new DelegatingJob(new ThreadStart(((IThreadRunnable) jobObject).Run));
}
else
{
throw new ArgumentException(

View File

@@ -50,15 +50,20 @@ namespace Spring.Scheduling.Quartz
/// <author>Alef Arendsen</author>
/// <seealso cref="Concurrent" />
/// <seealso cref="MethodInvokingFactoryObject" />
public class MethodInvokingJobDetailFactoryObject : ArgumentConvertingMethodInvoker, IFactoryObject, IObjectNameAware,
IInitializingObject
public class MethodInvokingJobDetailFactoryObject : ArgumentConvertingMethodInvoker,
IObjectFactoryAware,
IFactoryObject,
IObjectNameAware,
IInitializingObject
{
private string name;
private string group;
private bool concurrent = true;
private string[] jobListenerNames;
private string targetObjectName;
private string objectName;
private JobDetail jobDetail;
private IObjectFactory objectFactory;
/// <summary>
/// Initializes a new instance of the <see cref="MethodInvokingJobDetailFactoryObject"/> class.
@@ -146,6 +151,30 @@ namespace Spring.Scheduling.Quartz
}
/// <summary>
/// Set the name of the target object in the Spring object factory.
/// </summary>
/// <remarks>
/// This is an alternative to specifying <see cref="TargetObject" />
/// allowing for non-singleton objects to be invoked. Note that specified
/// "TargetObject" and <see cref="TargetType" /> values will
/// override the corresponding effect of this "targetObjectName" setting
///(i.e. statically pre-define the object type or even the target object).
/// </remarks>
public string TargetObjectName
{
set { targetObjectName = value; }
}
/// <summary>
/// Sets the object factory.
/// </summary>
/// <value>The object factory.</value>
public IObjectFactory ObjectFactory
{
set { objectFactory = value; }
}
/// <summary>
/// Return an instance (possibly shared or independent) of the object
/// managed by this factory.
/// </summary>
@@ -174,7 +203,18 @@ namespace Spring.Scheduling.Quartz
/// <value></value>
public virtual Type ObjectType
{
get { return typeof (JobDetail); }
get
{
if (targetObjectName != null)
{
if (objectFactory == null)
{
throw new InvalidOperationException("ObjectFactory must be set when using 'TargetObjectName'");
}
return objectFactory.GetType(targetObjectName);
}
return typeof (JobDetail);
}
}
/// <summary>

View File

@@ -0,0 +1,452 @@
/*
* Copyright 2002-2008 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 Common.Logging;
using Quartz;
using Quartz.Xml;
using Spring.Collections;
using Spring.Context;
using Spring.Core.IO;
using Spring.Transaction;
using Spring.Transaction.Support;
namespace Spring.Scheduling.Quartz
{
/// <summary>
/// Common base class for accessing a Quartz Scheduler, i.e. for registering jobs,
/// triggers and listeners on a <see cref="IScheduler" /> instance.
/// </summary>
/// <remarks>
/// <p>For concrete usage, check out the <see cref="SchedulerFactoryObject" /> and
/// <see cref="SchedulerAccessorObject" /> classes.
///</remarks>
/// <author>Juergen Hoeller</author>
/// <author>Marko Lahma (.NET)</author>
public abstract class SchedulerAccessor : IResourceLoaderAware
{
protected readonly ILog logger;
private bool overwriteExistingJobs;
private string[] jobSchedulingDataLocations;
private IList jobDetails;
private IDictionary calendars;
private IList triggers;
private ISchedulerListener[] schedulerListeners;
private IJobListener[] globalJobListeners;
private IJobListener[] jobListeners;
private ITriggerListener[] globalTriggerListeners;
private ITriggerListener[] triggerListeners;
private IPlatformTransactionManager transactionManager;
protected IResourceLoader resourceLoader;
protected SchedulerAccessor()
{
logger = LogManager.GetLogger(GetType());
}
/// <summary>
/// Set whether any jobs defined on this SchedulerFactoryObject should overwrite
/// existing job definitions. Default is "false", to not overwrite already
/// registered jobs that have been read in from a persistent job store.
/// </summary>
public virtual bool OverwriteExistingJobs
{
set { overwriteExistingJobs = value; }
}
/// <summary>
/// Set the locations of Quartz job definition XML files that follow the
/// "job_scheduling_data_1_5" XSD. Can be specified to automatically
/// register jobs that are defined in such files, possibly in addition
/// to jobs defined directly on this SchedulerFactoryObject.
/// </summary>
/// <seealso cref="JobSchedulingDataProcessor" />
public virtual string[] JobSchedulingDataLocations
{
set { jobSchedulingDataLocations = value; }
}
/// <summary>
/// Set the location of a Quartz job definition XML file that follows the
/// "job_scheduling_data" XSD. Can be specified to automatically
/// register jobs that are defined in such a file, possibly in addition
/// to jobs defined directly on this SchedulerFactoryObject.
/// </summary>
/// <seealso cref="ResourceJobSchedulingDataProcessor" />
/// <seealso cref="JobSchedulingDataProcessor" />
public virtual string JobSchedulingDataLocation
{
set { jobSchedulingDataLocations = new string[] {value}; }
}
/// <summary>
/// Register a list of JobDetail objects with the Scheduler that
/// this FactoryObject creates, to be referenced by Triggers.
/// <p>This is not necessary when a Trigger determines the JobDetail
/// itself: In this case, the JobDetail will be implicitly registered
/// in combination with the Trigger.
/// </summary>
/// <seealso cref="Triggers" />
/// <seealso cref="JobDetail" />
/// <seealso cref="JobDetailObject" />
/// <seealso cref="IJobDetailAwareTrigger" />
/// <seealso cref="Trigger.JobName" />
public virtual JobDetail[] JobDetails
{
set
{
// Use modifiable ArrayList here, to allow for further adding of
// JobDetail objects during autodetection of JobDetailAwareTriggers.
jobDetails = new ArrayList(value);
}
}
/// <summary>
/// Register a list of Quartz ICalendar objects with the Scheduler
/// that this FactoryObject creates, to be referenced by Triggers.
/// </summary>
/// <value>Map with calendar names as keys as Calendar objects as values</value>
/// <seealso cref="ICalendar" />
/// <seealso cref="Trigger.CalendarName" />
public virtual IDictionary Calendars
{
set { calendars = value; }
}
/// <summary>
// Register a list of Trigger objects with the Scheduler that
/// this FactoryObject creates.
/// </summary>
/// <remarks>
/// If the Trigger determines the corresponding JobDetail itself,
/// the job will be automatically registered with the Scheduler.
/// Else, the respective JobDetail needs to be registered via the
/// "jobDetails" property of this FactoryObject.
/// </remarks>
/// <seealso cref="JobDetails" />
/// <seealso cref="JobDetail" />
/// <seealso cref="IJobDetailAwareTrigger" />
/// <seealso cref="CronTriggerObject" />
/// <seealso cref="SimpleTriggerObject" />
public virtual Trigger[] Triggers
{
set { triggers = new ArrayList(value); }
}
/// <summary>
/// Specify Quartz SchedulerListeners to be registered with the Scheduler.
/// </summary>
public virtual ISchedulerListener[] SchedulerListeners
{
set { schedulerListeners = value; }
}
/// <summary>
/// Specify global Quartz JobListeners to be registered with the Scheduler.
/// Such JobListeners will apply to all Jobs in the Scheduler.
/// </summary>
public virtual IJobListener[] GlobalJobListeners
{
set { globalJobListeners = value; }
}
/// <summary>
/// Specify named Quartz JobListeners to be registered with the Scheduler.
/// Such JobListeners will only apply to Jobs that explicitly activate
/// them via their name.
/// </summary>
/// <seealso cref="IJobListener.Name" />
/// <seealso cref="JobDetail.AddJobListener" />
/// <seealso cref="JobDetailObject.JobListenerNames" />
public virtual IJobListener[] JobListeners
{
set { jobListeners = value; }
}
/// <summary>
/// Specify global Quartz TriggerListeners to be registered with the Scheduler.
/// Such TriggerListeners will apply to all Triggers in the Scheduler.
/// </summary>
public virtual ITriggerListener[] GlobalTriggerListeners
{
set { globalTriggerListeners = value; }
}
/// <summary>
/// Specify named Quartz TriggerListeners to be registered with the Scheduler.
/// Such TriggerListeners will only apply to Triggers that explicitly activate
/// them via their name.
/// </summary>
/// <seealso cref="ITriggerListener#getName" />
/// <seealso cref="Trigger#addTriggerListener" />
/// <seealso cref="CronTriggerObject#setTriggerListenerNames" />
/// <seealso cref="SimpleTriggerObject#setTriggerListenerNames" />
public virtual ITriggerListener[] TriggerListeners
{
set { triggerListeners = value; }
}
/// <summary>
/// Set the transaction manager to be used for registering jobs and triggers
/// that are defined by this SchedulerFactoryObject. Default is none; setting
/// this only makes sense when specifying a DataSource for the Scheduler.
/// </summary>
public virtual IPlatformTransactionManager TransactionManager
{
set { transactionManager = value; }
}
/// <summary>
/// Sets the <see cref="Spring.Core.IO.IResourceLoader"/>
/// that this object runs in.
/// </summary>
/// <value></value>
/// <remarks>
/// Invoked <b>after</b> population of normal objects 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 <b>before</b> setting
/// <see cref="Spring.Context.IApplicationContextAware"/>'s
/// <see cref="Spring.Context.IApplicationContextAware.ApplicationContext"/>
/// property.
/// </remarks>
public virtual IResourceLoader ResourceLoader
{
set { resourceLoader = value; }
}
/// <summary>
/// Register jobs and triggers (within a transaction, if possible).
/// </summary>
protected virtual void RegisterJobsAndTriggers()
{
ITransactionStatus transactionStatus = null;
if (transactionManager != null)
{
transactionStatus = transactionManager.GetTransaction(new DefaultTransactionDefinition());
}
try
{
if (jobSchedulingDataLocations != null)
{
JobSchedulingDataProcessor dataProcessor = new JobSchedulingDataProcessor(true, true);
for (int i = 0; i < this.jobSchedulingDataLocations.Length; i++)
{
dataProcessor.ProcessFileAndScheduleJobs(
jobSchedulingDataLocations[i], GetScheduler(), overwriteExistingJobs);
}
}
// Register JobDetails.
if (jobDetails != null)
{
foreach (JobDetail jobDetail in jobDetails)
{
AddJobToScheduler(jobDetail);
}
}
else
{
// Create empty list for easier checks when registering triggers.
jobDetails = new LinkedList();
}
// Register Calendars.
if (calendars != null)
{
foreach (DictionaryEntry entry in calendars)
{
string calendarName = (string) entry.Key;
ICalendar calendar = (ICalendar) entry.Value;
GetScheduler().AddCalendar(calendarName, calendar, true, true);
}
}
// Register Triggers.
if (triggers != null)
{
foreach (Trigger trigger in triggers)
{
AddTriggerToScheduler(trigger);
}
}
}
catch (Exception ex)
{
if (transactionStatus != null)
{
try
{
transactionManager.Rollback(transactionStatus);
}
catch (TransactionException)
{
logger.Error("Job registration exception overridden by rollback exception", ex);
throw;
}
}
if (ex is SchedulerException)
{
throw;
}
throw new SchedulerException("Registration of jobs and triggers failed: " + ex.Message);
}
if (transactionStatus != null)
{
transactionManager.Commit(transactionStatus);
}
}
/// <summary>
/// Add the given job to the Scheduler, if it doesn't already exist.
/// Overwrites the job in any case if "overwriteExistingJobs" is set.
/// </summary>
/// <param name="jobDetail">the job to add</param>
/// <returns><code>true</code> if the job was actually added, <code>false</code> if it already existed before</returns>
private bool AddJobToScheduler(JobDetail jobDetail)
{
if (overwriteExistingJobs ||
GetScheduler().GetJobDetail(jobDetail.Name, jobDetail.Group) == null)
{
GetScheduler().AddJob(jobDetail, true);
return true;
}
else
{
return false;
}
}
/// <summary>
/// Add the given trigger to the Scheduler, if it doesn't already exist.
/// Overwrites the trigger in any case if "overwriteExistingJobs" is set.
/// </summary>
/// <param name="trigger">the trigger to add</param>
/// <returns><code>true</code> if the trigger was actually added, <code>false</code> if it already existed before</returns>
private bool AddTriggerToScheduler(Trigger trigger)
{
bool triggerExists = (GetScheduler().GetTrigger(trigger.Name, trigger.Group) != null);
if (!triggerExists || this.overwriteExistingJobs)
{
// Check if the Trigger is aware of an associated JobDetail.
if (trigger is IJobDetailAwareTrigger)
{
JobDetail jobDetail = ((IJobDetailAwareTrigger) trigger).JobDetail;
// Automatically register the JobDetail too.
if (!jobDetails.Contains(jobDetail) && AddJobToScheduler(jobDetail))
{
jobDetails.Add(jobDetail);
}
}
if (!triggerExists)
{
try
{
GetScheduler().ScheduleJob(trigger);
}
catch (ObjectAlreadyExistsException ex)
{
if (logger.IsDebugEnabled)
{
logger.Debug(
"Unexpectedly found existing trigger, assumably due to cluster race condition: " +
ex.Message + " - can safely be ignored");
}
if (overwriteExistingJobs)
{
GetScheduler().RescheduleJob(trigger.Name, trigger.Group, trigger);
}
}
}
else
{
GetScheduler().RescheduleJob(trigger.Name, trigger.Group, trigger);
}
return true;
}
else
{
return false;
}
}
/// <summary>
/// Register all specified listeners with the Scheduler.
/// </summary>
protected virtual void RegisterListeners()
{
if (schedulerListeners != null)
{
for (int i = 0; i < schedulerListeners.Length; i++)
{
GetScheduler().AddSchedulerListener(schedulerListeners[i]);
}
}
if (globalJobListeners != null)
{
for (int i = 0; i < globalJobListeners.Length; i++)
{
GetScheduler().AddGlobalJobListener(globalJobListeners[i]);
}
}
if (jobListeners != null)
{
for (int i = 0; i < jobListeners.Length; i++)
{
GetScheduler().AddJobListener(jobListeners[i]);
}
}
if (globalTriggerListeners != null)
{
for (int i = 0; i < globalTriggerListeners.Length; i++)
{
GetScheduler().AddGlobalTriggerListener(globalTriggerListeners[i]);
}
}
if (triggerListeners != null)
{
for (int i = 0; i < triggerListeners.Length; i++)
{
GetScheduler().AddTriggerListener(triggerListeners[i]);
}
}
}
/// <summary>
/// Template method that determines the Scheduler to operate on.
/// To be implemented by subclasses.
/// </summary>
/// <returns></returns>
protected abstract IScheduler GetScheduler();
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2002-2008 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; }
}
protected override IScheduler GetScheduler()
{
return scheduler;
}
/// <summary>
/// Return the Quartz Scheduler instance that this accessor operates on.
/// </summary>
public IObjectFactory ObjectFactory
{
set { objectFactory = value; }
}
public void AfterPropertiesSet()
{
if (scheduler == null)
{
if (schedulerName != null)
{
scheduler = FindScheduler(schedulerName);
}
else
{
throw new InvalidOperationException("No Scheduler specified");
}
}
RegisterListeners();
RegisterJobsAndTriggers();
}
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

@@ -19,21 +19,16 @@ using System.Collections;
using System.Collections.Specialized;
using System.IO;
using Common.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Simpl;
using Quartz.Spi;
using Quartz.Util;
using Quartz.Xml;
using Spring.Context;
using Spring.Core.IO;
using Spring.Data.Common;
using Spring.Objects.Factory;
using Spring.Transaction;
using Spring.Transaction.Support;
namespace Spring.Scheduling.Quartz
{
@@ -78,7 +73,7 @@ namespace Spring.Scheduling.Quartz
/// <seealso cref="IScheduler" />
/// <seealso cref="ISchedulerFactory" />
/// <seealso cref="StdSchedulerFactory" />
public class SchedulerFactoryObject : IFactoryObject, IApplicationContextAware, IInitializingObject, IDisposable
public class SchedulerFactoryObject : SchedulerAccessor, IFactoryObject, IObjectNameAware, IApplicationContextAware, IInitializingObject, IDisposable
{
/// <summary>
/// Default thread count to be set to thread pool.
@@ -126,45 +121,28 @@ namespace Spring.Scheduling.Quartz
get { return configTimeTaskExecutor; }
}
/// <summary>
/// Logger for this instance and its sub-class instances.
/// </summary>
protected ILog logger;
private IApplicationContext applicationContext;
private string applicationContextSchedulerContextKey;
private bool autoStartup = true;
private IDictionary calendars;
private IResource configLocation;
private IJobListener[] globalJobListeners;
private ITriggerListener[] globalTriggerListeners;
private ArrayList jobDetails;
private IJobFactory jobFactory;
private bool jobFactorySet;
private IJobListener[] jobListeners;
private string[] jobSchedulingDataLocations;
private bool overwriteExistingJobs;
private IDictionary quartzProperties;
private IScheduler scheduler;
private IDictionary schedulerContextMap;
private Type schedulerFactoryType;
private ISchedulerListener[] schedulerListeners;
private string schedulerName;
private TimeSpan startupDelay = TimeSpan.Zero;
private ITaskExecutor taskExecutor;
private ITriggerListener[] triggerListeners;
private ArrayList triggers;
private bool exposeSchedulerInRepository;
private bool waitForJobsToCompleteOnShutdown;
private IDbProvider dbProvider;
private IPlatformTransactionManager transactionManager;
/// <summary>
/// Initializes a new instance of the <see cref="SchedulerFactoryObject"/> class.
/// </summary>
public SchedulerFactoryObject()
{
logger = LogManager.GetLogger(GetType());
schedulerFactoryType = typeof (StdSchedulerFactory);
}
@@ -325,149 +303,16 @@ namespace Spring.Scheduling.Quartz
}
}
/// <summary>
/// Set whether any jobs defined on this SchedulerFactoryObject should overwrite
/// existing job definitions. Default is "false", to not overwrite already
/// registered jobs that have been read in from a persistent job store.
/// </summary>
public virtual bool OverwriteExistingJobs
/// Set whether to expose the Spring-managed {@link Scheduler} instance in the
/// Quartz {@link SchedulerRepository}. Default is "false", since the Spring-managed
/// Scheduler is usually exclusively intended for access within the Spring context.
/// <p>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. Note that such global exposure was the accidental
/// default in earlier Spring versions; this has been fixed as of Spring 2.5.6.
public virtual bool ExposeSchedulerInRepository
{
set { overwriteExistingJobs = value; }
}
/// <summary>
/// Set the location of a Quartz job definition XML file that follows the
/// "job_scheduling_data" XSD. Can be specified to automatically
/// register jobs that are defined in such a file, possibly in addition
/// to jobs defined directly on this SchedulerFactoryObject.
/// </summary>
/// <seealso cref="ResourceJobSchedulingDataProcessor" />
/// <seealso cref="JobSchedulingDataProcessor" />
public virtual string JobSchedulingDataLocation
{
set { jobSchedulingDataLocations = new string[] {value}; }
}
/// <summary>
/// Set the locations of Quartz job definition XML files that follow the
/// "job_scheduling_data" XSD. Can be specified to automatically
/// register jobs that are defined in such files, possibly in addition
/// to jobs defined directly on this SchedulerFactoryObject.
/// </summary>
/// <value>The job scheduling data locations.</value>
/// <seealso cref="ResourceJobSchedulingDataProcessor"/>
/// <seealso cref="JobSchedulingDataProcessor"/>
public virtual string[] JobSchedulingDataLocations
{
set { jobSchedulingDataLocations = value; }
}
/// <summary>
/// Register a list of JobDetail objects with the Scheduler that
/// this FactoryObject creates, to be referenced by Triggers.
/// <p>This is not necessary when a Trigger determines the JobDetail
/// itself: In this case, the JobDetail will be implicitly registered
/// in combination with the Trigger.</p>
/// </summary>
/// <seealso cref="Triggers" />
/// <seealso cref="JobDetail" />
/// <seealso cref="JobDetailObject" />
/// <seealso cref="IJobDetailAwareTrigger" />
/// <seealso cref="Trigger.JobName" />
public virtual JobDetail[] JobDetails
{
set
{
// Use modifiable ArrayList here, to allow for further adding of
// JobDetail objects during autodetection of JobDetailAwareTriggers.
jobDetails = new ArrayList(value);
}
}
/// <summary>
/// Register a list of Quartz ICalendar objects with the Scheduler
/// that this FactoryObject creates, to be referenced by Triggers.
/// </summary>
/// <value>The calendars.</value>
/// <seealso cref="ICalendar"/>
/// <seealso cref="Trigger.CalendarName"/>
public virtual IDictionary Calendars
{
set { calendars = value; }
}
/// <summary>
/// Register a list of Trigger objects with the Scheduler that
/// this FactoryObject creates.
/// <p>
/// If the Trigger determines the corresponding JobDetail itself,
/// the job will be automatically registered with the Scheduler.
/// Else, the respective JobDetail needs to be registered via the
/// "jobDetails" property of this FactoryObject.
/// </p>
/// </summary>
/// <seealso cref="JobDetails" />
/// <seealso cref="JobDetail" />
/// <seealso cref="IJobDetailAwareTrigger" />
/// <seealso cref="CronTriggerObject" />
public virtual Trigger[] Triggers
{
set { triggers = new ArrayList(value); }
}
/// <summary>
/// Specify Quartz SchedulerListeners to be registered with the Scheduler.
/// </summary>
public virtual ISchedulerListener[] SchedulerListeners
{
set { schedulerListeners = value; }
}
/// <summary>
/// Specify global Quartz JobListeners to be registered with the Scheduler.
/// Such JobListeners will apply to all Jobs in the Scheduler.
/// </summary>
public virtual IJobListener[] GlobalJobListeners
{
set { globalJobListeners = value; }
}
/// <summary>
/// Specify named Quartz JobListeners to be registered with the Scheduler.
/// Such JobListeners will only apply to Jobs that explicitly activate
/// them via their name.
/// </summary>
/// <value>The job listeners.</value>
/// <seealso cref="IJobListener.Name"/>
/// <seealso cref="JobDetail.AddJobListener"/>
/// <seealso cref="JobDetail.JobListenerNames"/>
public virtual IJobListener[] JobListeners
{
set { jobListeners = value; }
}
/// <summary>
/// Specify global Quartz TriggerListeners to be registered with the Scheduler.
/// Such TriggerListeners will apply to all Triggers in the Scheduler.
/// </summary>
/// <value>The global trigger listeners.</value>
public virtual ITriggerListener[] GlobalTriggerListeners
{
set { globalTriggerListeners = value; }
}
/// <summary>
/// Specify named Quartz TriggerListeners to be registered with the Scheduler.
/// Such TriggerListeners will only apply to Triggers that explicitly activate
/// them via their name.
/// </summary>
/// <seealso cref="ITriggerListener.Name" />
/// <seealso cref="Trigger.AddTriggerListener" />
/// <seealso cref="Trigger.TriggerListenerNames" />
/// <seealso cref="Trigger.TriggerListenerNames" />
public virtual ITriggerListener[] TriggerListeners
{
set { triggerListeners = value; }
set { exposeSchedulerInRepository = value; }
}
/// <summary>
@@ -524,7 +369,6 @@ namespace Spring.Scheduling.Quartz
/// </p>
/// </remarks>
/// <seealso cref="QuartzProperties" />
/// <seealso cref="TransactionManager" />
/// <seealso cref="LocalDataSourceJobStore" />
public IDbProvider DbProvider
{
@@ -532,15 +376,27 @@ namespace Spring.Scheduling.Quartz
}
/// <summary>
/// Set the transaction manager to be used for registering jobs and triggers
/// that are defined by this SchedulerFactoryObject. Default is none; setting
/// this only makes sense when specifying a DataSource for the Scheduler.
/// Set the name of the object in the object factory that created this object.
/// </summary>
/// <seealso cref="DbProvider" />
public IPlatformTransactionManager TransactionManager
{
set { transactionManager = value; }
}
/// <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.
@@ -615,6 +471,16 @@ namespace Spring.Scheduling.Quartz
#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>
@@ -633,7 +499,7 @@ namespace Spring.Scheduling.Quartz
/// probably fatal) exception.
/// </note>
/// </remarks>
public object GetObject()
public virtual object GetObject()
{
return scheduler;
}
@@ -720,7 +586,7 @@ namespace Spring.Scheduling.Quartz
if (!jobFactorySet && !(scheduler is RemoteScheduler))
{
// Use AdaptableJobFactory as default for a local Scheduler, unless when
// explicitly given a null value through the "jobFactory" bean property.
// explicitly given a null value through the "jobFactory" object property.
jobFactory = new AdaptableJobFactory();
}
@@ -763,70 +629,72 @@ namespace Spring.Scheduling.Quartz
/// <param name="schedulerFactory">the SchedulerFactory to Initialize</param>
private void InitSchedulerFactory(ISchedulerFactory schedulerFactory)
{
if (configLocation != null || quartzProperties != null || schedulerName != null ||
taskExecutor != null || dbProvider != null)
if (!(schedulerFactory is StdSchedulerFactory))
{
if (!(schedulerFactory is StdSchedulerFactory))
if (configLocation != null || quartzProperties != null || schedulerName != null ||
taskExecutor != null || dbProvider != null)
{
throw new ArgumentException("StdSchedulerFactory required for applying Quartz properties");
throw new ArgumentException("StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);
}
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).Name);
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);
// 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>
@@ -859,10 +727,24 @@ namespace Spring.Scheduling.Quartz
/// <seealso cref="ISchedulerFactory.GetScheduler()"/>
protected virtual IScheduler CreateScheduler(ISchedulerFactory schedulerFactory, string schedName)
{
// StdSchedulerFactory's default "getScheduler" implementation
// uses the scheduler name specified in the Quartz properties,
// which we have set before (in "InitSchedulerFactory").
return schedulerFactory.GetScheduler();
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>
@@ -890,212 +772,6 @@ namespace Spring.Scheduling.Quartz
}
/// <summary>
/// Register all specified listeners with the Scheduler.
/// </summary>
private void RegisterListeners()
{
if (schedulerListeners != null)
{
for (int i = 0; i < schedulerListeners.Length; i++)
{
scheduler.AddSchedulerListener(schedulerListeners[i]);
}
}
if (globalJobListeners != null)
{
for (int i = 0; i < globalJobListeners.Length; i++)
{
scheduler.AddGlobalJobListener(globalJobListeners[i]);
}
}
if (jobListeners != null)
{
for (int i = 0; i < jobListeners.Length; i++)
{
scheduler.AddJobListener(jobListeners[i]);
}
}
if (globalTriggerListeners != null)
{
for (int i = 0; i < globalTriggerListeners.Length; i++)
{
scheduler.AddGlobalTriggerListener(globalTriggerListeners[i]);
}
}
if (triggerListeners != null)
{
for (int i = 0; i < triggerListeners.Length; i++)
{
scheduler.AddTriggerListener(triggerListeners[i]);
}
}
}
/// <summary>
/// Register jobs and triggers (within a transaction, if possible).
/// </summary>
private void RegisterJobsAndTriggers()
{
ITransactionStatus transactionStatus = null;
if (transactionManager != null) {
transactionStatus = transactionManager.GetTransaction(new DefaultTransactionDefinition());
}
try
{
if (jobSchedulingDataLocations != null)
{
ResourceJobSchedulingDataProcessor dataProcessor = new ResourceJobSchedulingDataProcessor();
if (applicationContext != null)
{
dataProcessor.ResourceLoader = applicationContext;
}
for (int i = 0; i < jobSchedulingDataLocations.Length; i++)
{
dataProcessor.ProcessFileAndScheduleJobs(jobSchedulingDataLocations[i], scheduler,
overwriteExistingJobs);
}
}
// Register JobDetails.
if (jobDetails != null)
{
foreach (JobDetail jobDetail in jobDetails)
{
AddJobToScheduler(jobDetail);
}
}
else
{
// Create empty list for easier checks when registering triggers.
jobDetails = new ArrayList();
}
// Register Calendars.
if (calendars != null)
{
foreach (DictionaryEntry entry in calendars)
{
string calendarName = (string) entry.Key;
ICalendar calendar = (ICalendar) entry.Value;
scheduler.AddCalendar(calendarName, calendar, true, true);
}
}
// Register Triggers.
if (triggers != null)
{
foreach (Trigger trigger in triggers)
{
AddTriggerToScheduler(trigger);
}
}
}
catch (Exception ex)
{
if (transactionStatus != null)
{
try
{
transactionManager.Rollback(transactionStatus);
}
catch (TransactionException)
{
logger.Error("Job registration exception overridden by rollback exception", ex);
throw;
}
}
if (ex is SchedulerException)
{
throw;
}
throw new SchedulerException("Registration of jobs and triggers failed: " + ex.Message, ex);
}
if (transactionStatus != null)
{
transactionManager.Commit(transactionStatus);
}
}
/// <summary>
/// Add the given job to the Scheduler, if it doesn't already exist.
/// Overwrites the job in any case if "overwriteExistingJobs" is set.
/// </summary>
/// <param name="jobDetail">the job to add</param>
/// <returns>
/// <code>true</code> if the job was actually added,
/// <code>false</code> if it already existed before
/// </returns>
/// <seealso cref="OverwriteExistingJobs"/>
private bool AddJobToScheduler(JobDetail jobDetail)
{
if (overwriteExistingJobs || scheduler.GetJobDetail(jobDetail.Name, jobDetail.Group) == null)
{
scheduler.AddJob(jobDetail, true);
return true;
}
return false;
}
/// <summary>
/// Add the given trigger to the Scheduler, if it doesn't already exist.
/// Overwrites the trigger in any case if "overwriteExistingJobs" is set.
/// </summary>
/// <param name="trigger">the trigger to add</param>
/// <returns>
/// <code>true</code> if the trigger was actually added,
/// <code>false</code> if it already existed before
/// </returns>
/// <seealso cref="OverwriteExistingJobs"/>
private bool AddTriggerToScheduler(Trigger trigger)
{
bool triggerExists = (scheduler.GetTrigger(trigger.Name, trigger.Group) != null);
if (!triggerExists || overwriteExistingJobs)
{
// Check if the Trigger is aware of an associated JobDetail.
if (trigger is IJobDetailAwareTrigger)
{
JobDetail jobDetail = ((IJobDetailAwareTrigger) trigger).JobDetail;
// Automatically register the JobDetail too.
if (!jobDetails.Contains(jobDetail) && AddJobToScheduler(jobDetail))
{
jobDetails.Add(jobDetail);
}
}
if (!triggerExists)
{
try
{
scheduler.ScheduleJob(trigger);
}
catch (ObjectAlreadyExistsException ex)
{
if (logger.IsDebugEnabled)
{
logger.Debug(
string.Format(
"Unexpectedly found existing trigger, assumably due to cluster race condition: {0} - can safely be ignored",
ex.Message));
}
if (overwriteExistingJobs)
{
scheduler.RescheduleJob(trigger.Name, trigger.Group, trigger);
}
}
}
else
{
scheduler.RescheduleJob(trigger.Name, trigger.Group, trigger);
}
return true;
}
return false;
}
/// <summary>
/// Start the Quartz Scheduler, respecting the "startDelay" setting.
/// </summary>

View File

@@ -62,6 +62,8 @@
<Compile Include="Scheduling\Quartz\MethodInvokingRunnable.cs" />
<Compile Include="Scheduling\Quartz\QuartzJobObject.cs" />
<Compile Include="Scheduling\Quartz\ResourceJobSchedulingDataProcessor.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" />

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{E823D54C-CE82-4868-929F-5F95A999F61E}</ProjectGuid>
<OutputType>Library</OutputType>
@@ -58,6 +58,8 @@
<Compile Include="Scheduling\Quartz\MethodInvokingRunnable.cs" />
<Compile Include="Scheduling\Quartz\QuartzJobObject.cs" />
<Compile Include="Scheduling\Quartz\ResourceJobSchedulingDataProcessor.cs" />
<Compile Include="Scheduling\Quartz\SchedulerAccessorObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulerAccessor.cs" />
<Compile Include="Scheduling\Quartz\SchedulerFactoryObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulingException.cs" />
<Compile Include="Scheduling\Quartz\SimpleThreadPoolTaskExecutor.cs" />

View File

@@ -0,0 +1,31 @@
namespace Spring.Scheduling.Quartz
{
/// <summary>
/// </summary>
/// <author>Rob Harrop</author>
public class QuartzTestObject
{
private int exportCount;
private int importCount;
public void DoImport()
{
++importCount;
}
public void DoExport()
{
++exportCount;
}
public int ImportCount
{
get { return importCount; }
}
public int ExportCount
{
get { return exportCount; }
}
}
}

View File

@@ -50,6 +50,11 @@ namespace Spring.Scheduling.Quartz
{
factory = new SchedulerFactoryObject();
TestSchedulerFactory.Mockery.BackToRecordAll();
Expect
.Call(TestSchedulerFactory.MockScheduler.SchedulerName)
.Return("scheduler")
.Repeat.Any();
}
[Test]

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2005 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>
/// </summary>
/// <author>Juergen Hoeller</author>
public class TestMethodInvokingTask
{
public int counter;
private readonly object lockObject = new object();
public void DoSomething()
{
counter++;
}
public void DoWait()
{
counter++;
// wait until stop is called
lock (lockObject)
{
try
{
Monitor.Wait(lockObject);
}
catch (ThreadInterruptedException)
{
// fall through
}
}
}
public void Stop()
{
lock (lockObject)
{
Monitor.Pulse(lockObject);
}
}
}
}

View File

@@ -20,6 +20,8 @@ using NUnit.Framework;
using Quartz;
using Spring.Objects.Factory;
namespace Spring.Scheduling.Quartz
{
/// <summary>
@@ -40,6 +42,8 @@ namespace Spring.Scheduling.Quartz
[Test]
public virtual void TestAfterPropertiesSet_Defaults()
{
((IInitializingObject) trigger).AfterPropertiesSet();
Assert.AreEqual(TRIGGER_NAME, trigger.Name, "trigger name mismatch");
Assert.AreEqual(SchedulerConstants.DefaultGroup, trigger.Group, "trigger group name mismatch");
AssertDateTimesEqualityWithAllowedDelta(DateTime.UtcNow, trigger.StartTimeUtc, 1000);
@@ -50,6 +54,8 @@ namespace Spring.Scheduling.Quartz
[Test]
public virtual void TestAfterPropertiesSet_ValuesGiven()
{
((IInitializingObject)trigger).AfterPropertiesSet();
const string NAME = "newName";
const string GROUP = "newGroup";
DateTime START_TIME = new DateTime(10000000);
@@ -65,6 +71,8 @@ namespace Spring.Scheduling.Quartz
[Test]
public virtual void TestAfterPropertiesSet_JobDetailGiven()
{
((IInitializingObject)trigger).AfterPropertiesSet();
const string jobName = "jobName";
const string jobGroup = "jobGroup";
Assert.AreEqual(jobName, trigger.JobName, "trigger job name was not from job detail");
@@ -74,7 +82,9 @@ namespace Spring.Scheduling.Quartz
[Test]
public virtual void TestTriggerListenerNames_Valis()
{
string[] LISTENER_NAMES = new string[] {"Foo", "Bar", "Baz"};
((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");
}

View File

@@ -58,11 +58,18 @@
<Project>{E823D54C-CE82-4868-929F-5F95A999F61E}</Project>
<Name>Spring.Scheduling.Quartz.2005</Name>
</ProjectReference>
<ProjectReference Include="..\Spring.Core.Tests\Spring.Core.Tests.2005.csproj">
<Project>{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}</Project>
<Name>Spring.Core.Tests.2005</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Scheduling\Quartz\AdaptableJobFactoryTest.cs" />
<Compile Include="Scheduling\Quartz\QuartzSupportTests.cs" />
<Compile Include="Scheduling\Quartz\QuartzTestObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulerFactoryObjectTest.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" />
<Compile Include="Scheduling\Quartz\SimpleTriggerObjectTest.cs" />
@@ -76,6 +83,23 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="job-scheduling-data.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="multipleAnonymousMethodInvokingJobDetailFB.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="multipleSchedulers.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="schedulerAccessorObject.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="schedulerRepositoryExposure.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</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.

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}</ProjectGuid>
<OutputType>Library</OutputType>
@@ -54,11 +54,18 @@
<Project>{E823D54C-CE82-4868-929F-5F95A999F61E}</Project>
<Name>Spring.Scheduling.Quartz.2008</Name>
</ProjectReference>
<ProjectReference Include="..\Spring.Core.Tests\Spring.Core.Tests.2008.csproj">
<Project>{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}</Project>
<Name>Spring.Core.Tests.2008</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Scheduling\Quartz\AdaptableJobFactoryTest.cs" />
<Compile Include="Scheduling\Quartz\QuartzSupportTests.cs" />
<Compile Include="Scheduling\Quartz\QuartzTestObject.cs" />
<Compile Include="Scheduling\Quartz\SchedulerFactoryObjectTest.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" />
<Compile Include="Scheduling\Quartz\SimpleTriggerObjectTest.cs" />
@@ -72,6 +79,23 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="job-scheduling-data.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="multipleAnonymousMethodInvokingJobDetailFB.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="multipleSchedulers.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="schedulerAccessorObject.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="schedulerRepositoryExposure.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</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.

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<quartz xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0">
<job>
<job-detail>
<name>myJob</name>
<group>myGroup</group>
<job-type>Spring.Scheduling.Quartz.DummyJob, Spring.Scheduling.Quartz.Tests</job-type>
<job-data-map>
<entry>
<key>param</key>
<value>10</value>
</entry>
</job-data-map>
</job-detail>
<trigger>
<simple>
<name>myTrigger</name>
<group>myGroup</group>
<repeat-count>1</repeat-count>
<repeat-interval>500</repeat-interval>
</simple>
</trigger>
</job>
</quartz>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<object id="scheduler" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<property name="Triggers">
<list>
<ref local="exportTrigger"/>
<ref local="importTrigger"/>
</list>
</property>
</object>
<object id="exportTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<property name="JobDetail">
<object type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<property name="TargetObject" ref="exportService"/>
<property name="TargetMethod" value="DoExport"/>
</object>
</property>
<property name="RepeatInterval" value="1s"/>
<property name="RepeatCount" value="1"/>
</object>
<object id="importTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<property name="JobDetail">
<object type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<property name="TargetObject" ref="importService"/>
<property name="TargetMethod" value="DoImport"/>
</object>
</property>
<property name="RepeatInterval" value="1s"/>
<property name="RepeatCount" value="1"/>
</object>
<object id="exportService" type="Spring.Scheduling.Quartz.QuartzTestObject, Spring.Scheduling.Quartz.Tests"/>
<object id="importService" type="Spring.Scheduling.Quartz.QuartzTestObject, Spring.Scheduling.Quartz.Tests"/>
</objects>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<object id="scheduler1" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<property name="SchedulerName" value="quartz1"/>
</object>
<object id="scheduler2" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<property name="SchedulerName" value="quartz2"/>
</object>
</objects>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<object id="scheduler" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"/>
<object type="Spring.Scheduling.Quartz.SchedulerAccessorObject, Spring.Scheduling.Quartz">
<property name="Scheduler" ref="scheduler"/>
<property name="Triggers">
<list>
<ref local="exportTrigger"/>
<ref local="importTrigger"/>
</list>
</property>
</object>
<object id="exportTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<property name="JobDetail">
<object type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<property name="TargetObject" ref="exportService"/>
<property name="TargetMethod" value="DoExport"/>
</object>
</property>
<property name="RepeatInterval" value="1s"/>
<property name="RepeatCount" value="1"/>
</object>
<object id="importTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<property name="jobDetail">
<object type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<property name="TargetObject" ref="importService"/>
<property name="TargetMethod" value="DoImport"/>
</object>
</property>
<property name="RepeatInterval" value="1s"/>
<property name="RepeatCount" value="1"/>
</object>
<object id="exportService" type="Spring.Scheduling.Quartz.QuartzTestObject, Spring.Scheduling.Quartz.Tests"/>
<object id="importService" type="Spring.Scheduling.Quartz.QuartzTestObject, Spring.Scheduling.Quartz.Tests"/>
</objects>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<object id="scheduler" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<property name="schedulerName" value="myScheduler"/>
<property name="exposeSchedulerInRepository" value="true"/>
</object>
<object type="Spring.Scheduling.Quartz.SchedulerAccessorObject, Spring.Scheduling.Quartz">
<property name="schedulerName" value="myScheduler"/>
<property name="triggers">
<list>
<ref local="exportTrigger"/>
<ref local="importTrigger"/>
</list>
</property>
</object>
<object id="exportTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<property name="JobDetail">
<object type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<property name="TargetObject" ref="exportService"/>
<property name="TargetMethod" value="DoExport"/>
</object>
</property>
<property name="RepeatInterval" value="1s"/>
<property name="RepeatCount" value="1"/>
</object>
<object id="importTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<property name="jobDetail">
<object type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<property name="TargetObject" ref="importService"/>
<property name="TargetMethod" value="DoImport"/>
</object>
</property>
<property name="RepeatInterval" value="1s"/>
<property name="RepeatCount" value="1"/>
</object>
<object id="exportService" type="Spring.Scheduling.Quartz.QuartzTestObject, Spring.Scheduling.Quartz.Tests"/>
<object id="importService" type="Spring.Scheduling.Quartz.QuartzTestObject, Spring.Scheduling.Quartz.Tests"/>
</objects>