From 0c1ab68f9350868659345c49663f6b3d72f47cb9 Mon Sep 17 00:00:00 2001 From: lahma Date: Thu, 31 Jul 2008 13:54:19 +0000 Subject: [PATCH] Starting to craft LocalDataSourceJobStore for Spring managed transactions --- .../solutions/Spring.Net.Quartz.2005.sln | 147 ++++++++++++++ .../Quartz/LocalDataSourceJobStore.cs | 114 +++++++++++ .../Quartz/SchedulerFactoryObject.cs | 181 ++++++++++++++---- .../Quartz/SpringDbProviderAdapter.cs | 77 ++++++++ .../Spring.Scheduling.Quartz.2005.csproj | 7 + 5 files changed, 494 insertions(+), 32 deletions(-) create mode 100644 build-support/solutions/Spring.Net.Quartz.2005.sln create mode 100644 src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalDataSourceJobStore.cs create mode 100644 src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringDbProviderAdapter.cs diff --git a/build-support/solutions/Spring.Net.Quartz.2005.sln b/build-support/solutions/Spring.Net.Quartz.2005.sln new file mode 100644 index 00000000..2625b8f0 --- /dev/null +++ b/build-support/solutions/Spring.Net.Quartz.2005.sln @@ -0,0 +1,147 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2005", "..\..\src\Spring\Spring.Core\Spring.Core.2005.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.2005", "..\..\src\Spring\Spring.Aop\Spring.Aop.2005.csproj", "{3A3A4E65-45A6-4B20-B460-0BEDC302C02C}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F04753EF-7A1B-4837-AB63-8C0821E8155D}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(SolutionItems) = preProject + Spring.build = Spring.build + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.2005", "..\..\src\Spring\Spring.Data\Spring.Data.2005.csproj", "{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.Tests.2005", "..\..\test\Spring\Spring.Data.Tests\Spring.Data.Tests.2005.csproj", "{ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + 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 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Scheduling.Quartz.2005", "..\..\src\Spring\Spring.Scheduling.Quartz\Spring.Scheduling.Quartz.2005.csproj", "{E823D54C-CE82-4868-929F-5F95A999F61E}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Scheduling.Quartz.Tests.2005", "..\..\test\Spring\Spring.Scheduling.Quartz.Tests\Spring.Scheduling.Quartz.Tests.2005.csproj", "{9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|.NET.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|.NET.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|.NET.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|.NET.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|.NET.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|.NET.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|.NET.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|.NET.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Any CPU.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|.NET.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|.NET.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|.NET.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|.NET.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Any CPU.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|.NET.ActiveCfg = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|.NET.ActiveCfg = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Any CPU.Build.0 = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.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 + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|.NET.ActiveCfg = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|.NET.ActiveCfg = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Any CPU.Build.0 = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|.NET.ActiveCfg = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|.NET.ActiveCfg = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Any CPU.Build.0 = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Mixed Platforms.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + NAntAddinLastFileName = Spring.build + EndGlobalSection +EndGlobal diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalDataSourceJobStore.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalDataSourceJobStore.cs new file mode 100644 index 00000000..315931a5 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalDataSourceJobStore.cs @@ -0,0 +1,114 @@ +/* + * Copyright 2002-2007 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.Spi; +using Quartz.Util; + +using Spring.Data.Support; + +namespace Spring.Scheduling.Quartz +{ + + /// + /// Subclass of Quartz's JobStoreCMT class that delegates to a Spring-managed + /// DataSource instead of using a Quartz-managed connection pool. This JobStore + /// will be used if SchedulerFactoryBean's "dbProvider" property is set. + /// + /// + ///

Supports both transactional and non-transactional DataSource access. + /// With a non-XA DataSource and local Spring transactions, a single DataSource + /// argument is sufficient. In case of an XA DataSource and global JTA transactions, + /// SchedulerFactoryBean's "nonTransactionalDataSource" property should be set, + /// passing in a non-XA DataSource that will not participate in global transactions.

+ /// + ///

Operations performed by this JobStore will properly participate in any + /// kind of Spring-managed transaction, as it uses Spring's DataSourceUtils + /// connection handling methods that are aware of a current transaction.

+ /// + ///

Note that all Quartz Scheduler operations that affect the persistent + /// job store should usually be performed within active transactions, + /// as they assume to get proper locks etc.

+ ///
+ /// Juergen Hoeller + /// Marko Lahma (.NET) + /// + /// + /// + public class LocalDataSourceJobStore : JobStoreSupport + { + + /** + * Name used for the transactional ConnectionProvider for Quartz. + * This provider will delegate to the local Spring-managed DataSource. + * @see org.quartz.utils.DBConnectionManager#addConnectionProvider + * @see SchedulerFactoryBean#setDataSource + */ + public const string TX_DATA_SOURCE_PREFIX = "springTxDataSource."; + + private Data.Common.IDbProvider dbProvider; + + public Data.Common.IDbProvider DbProvider + { + get { return dbProvider; } + set { dbProvider = value; } + } + + + public override void Initialize(ITypeLoadHelper loadHelper, ISchedulerSignaler signaler) + { + + // 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"); + } + + // Configure transactional connection settings for Quartz. + DataSource = TX_DATA_SOURCE_PREFIX + InstanceName; + DontSetAutoCommitFalse = true; + + // Register transactional ConnectionProvider for Quartz. + DBConnectionManager.Instance.AddConnectionProvider( + TX_DATA_SOURCE_PREFIX + InstanceName, new SpringDbProviderAdapter(dbProvider)); + + base.Initialize(loadHelper, signaler); + } + + protected override ConnectionAndTransactionHolder GetNonManagedTXConnection() + { + return GetConnection(); + } + + protected override object ExecuteInLock(string lockName, ITransactionCallback txCallback) + { + throw new System.NotImplementedException(); + } + + + protected override void CloseConnection(ConnectionAndTransactionHolder connectionAndTransactionHolder) + { + // Will work for transactional and non-transactional connections. + ConnectionUtils.DisposeConnection(connectionAndTransactionHolder.Connection, dbProvider); + } + + } + +} diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs index 4dd3e9e2..28f3f2e8 100644 --- a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs @@ -30,7 +30,10 @@ 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 { @@ -77,8 +80,6 @@ namespace Spring.Scheduling.Quartz /// public class SchedulerFactoryObject : IFactoryObject, IApplicationContextAware, IInitializingObject, IDisposable { - private const string CONTEXT_KEY_CONFIG_TIME_TASK_EXECUTOR = "sched_factory_conf_t_t_exec"; - /// /// Default thread count to be set to thread pool. /// @@ -89,6 +90,43 @@ namespace Spring.Scheduling.Quartz /// public const string PROP_THREAD_COUNT = "quartz.threadPool.threadCount"; + [ThreadStatic] + private static IDbProvider configTimeDbProvider; + + [ThreadStatic] + private static ITaskExecutor configTimeTaskExecutor; + + /// + /// Return the IDbProvider for the currently configured Quartz Scheduler, + /// to be used by LocalDataSourceJobStore. + /// + /// + /// This instance will be set before initialization of the corresponding + /// Scheduler, and reset immediately afterwards. It is thus only available + /// during configuration. + /// + /// + /// + public static IDbProvider ConfigTimeDbProvider + { + get { return configTimeDbProvider; } + } + + /// + /// Return the TaskExecutor for the currently configured Quartz Scheduler, + /// to be used by LocalTaskExecutorThreadPool. + /// + /// + /// This instance will be set before initialization of the corresponding + /// Scheduler, and reset immediately afterwards. It is thus only available + /// during configuration. + /// + public static ITaskExecutor ConfigTimeTaskExecutor + { + get { return configTimeTaskExecutor; } + } + + /// /// Logger for this instance and its sub-class instances. /// @@ -118,6 +156,8 @@ namespace Spring.Scheduling.Quartz private ITriggerListener[] triggerListeners; private ArrayList triggers; private bool waitForJobsToCompleteOnShutdown; + private IDbProvider dbProvider; + private IPlatformTransactionManager transactionManager; /// /// Initializes a new instance of the class. @@ -466,6 +506,42 @@ namespace Spring.Scheduling.Quartz set { waitForJobsToCompleteOnShutdown = value; } } + /// + /// Set the default DbProvider to be used by the Scheduler. If set, + /// this will override corresponding settings in Quartz properties. + /// + /// + ///

+ /// Note: If this is set, the Quartz settings should not define + /// a job store "dataSource" to avoid meaningless double configuration. + ///

+ ///

+ /// A Spring-specific subclass of Quartz' JobStoreSupport will be used. + /// It is therefore strongly recommended to perform all operations on + /// the Scheduler within Spring-managed transactions. + /// Else, database locking will not properly work and might even break + /// (e.g. if trying to obtain a lock on Oracle without a transaction). + ///

+ ///
+ /// + /// + /// + public IDbProvider DbProvider + { + set { dbProvider = value; } + } + + /// + /// Set the 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. + /// + /// + public IPlatformTransactionManager TransactionManager + { + set { transactionManager = value; } + } + /// /// Gets a value indicating whether this is running. /// @@ -489,20 +565,6 @@ namespace Spring.Scheduling.Quartz } } - /// - /// Return the TaskExecutor for the currently configured Quartz Scheduler, - /// to be used by LocalTaskExecutorThreadPool. - /// - /// - /// This instance will be set before initialization of the corresponding - /// Scheduler, and reset immediately afterwards. It is thus only available - /// during configuration. - /// - public static ITaskExecutor ConfigTimeTaskExecutor - { - get { return (ITaskExecutor) LogicalThreadContext.GetData(CONTEXT_KEY_CONFIG_TIME_TASK_EXECUTOR); } - } - #region IApplicationContextAware Members /// @@ -637,25 +699,51 @@ namespace Spring.Scheduling.Quartz ISchedulerFactory schedulerFactory = (ISchedulerFactory) ObjectUtils.InstantiateType(schedulerFactoryType); InitSchedulerFactory(schedulerFactory); - - // Get Scheduler instance from SchedulerFactory. - scheduler = CreateScheduler(schedulerFactory, schedulerName); - PopulateSchedulerContext(); - - if (!jobFactorySet && !(scheduler is RemoteScheduler)) + + if (taskExecutor != null) { - // Use AdaptableJobFactory as default for a local Scheduler, unless when - // explicitly given a null value through the "jobFactory" bean property. - jobFactory = new AdaptableJobFactory(); - } - - if (jobFactory != null) + // Make given TaskExecutor available for SchedulerFactory configuration. + configTimeTaskExecutor = taskExecutor; + } + if (dbProvider != null) { - if (jobFactory is ISchedulerContextAware) + // 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)) { - ((ISchedulerContextAware) jobFactory).SchedulerContext = scheduler.Context; + // Use AdaptableJobFactory as default for a local Scheduler, unless when + // explicitly given a null value through the "jobFactory" bean property. + jobFactory = new AdaptableJobFactory(); + } + + if (jobFactory != null) + { + if (jobFactory is ISchedulerContextAware) + { + ((ISchedulerContextAware) jobFactory).SchedulerContext = scheduler.Context; + } + scheduler.JobFactory = jobFactory; } - scheduler.JobFactory = jobFactory; + } + finally + { + if (taskExecutor != null) + { + configTimeTaskExecutor = null; + } + if (dbProvider != null) + { + configTimeDbProvider = null; + } } RegisterListeners(); @@ -677,7 +765,7 @@ namespace Spring.Scheduling.Quartz private void InitSchedulerFactory(ISchedulerFactory schedulerFactory) { if (configLocation != null || quartzProperties != null || schedulerName != null || - taskExecutor != null) + taskExecutor != null || dbProvider != null) { if (!(schedulerFactory is StdSchedulerFactory)) { @@ -726,6 +814,11 @@ namespace Spring.Scheduling.Quartz MergePropertiesIntoMap(quartzProperties, mergedProps); } + if (dbProvider != null) + { + mergedProps.Add(StdSchedulerFactory.PropertyJobStoreType, typeof(LocalDataSourceJobStore).FullName); + } + // Make sure to set the scheduler name as configured in the Spring configuration. if (schedulerName != null) @@ -845,6 +938,11 @@ namespace Spring.Scheduling.Quartz /// private void RegisterJobsAndTriggers() { + ITransactionStatus transactionStatus = null; + if (transactionManager != null) { + transactionStatus = transactionManager.GetTransaction(new DefaultTransactionDefinition()); + } + try { if (jobSchedulingDataLocations != null) @@ -897,12 +995,30 @@ namespace Spring.Scheduling.Quartz } 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); + } } /// @@ -1046,5 +1162,6 @@ namespace Spring.Scheduling.Quartz } } } + } } diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringDbProviderAdapter.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringDbProviderAdapter.cs new file mode 100644 index 00000000..8df05bff --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringDbProviderAdapter.cs @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2007 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.Data; + +using Quartz.Impl.AdoJobStore; +using Quartz.Impl.AdoJobStore.Common; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Adapts Spring's to Quartz's + /// . + /// + public class SpringDbProviderAdapter : IDbProvider + { + private readonly Data.Common.IDbProvider dbProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The Spring db provider. + public SpringDbProviderAdapter(Data.Common.IDbProvider dbProvider) + { + this.dbProvider = dbProvider; + } + + public IDbCommand CreateCommand() + { + return dbProvider.CreateCommand(); + } + + public object CreateCommandBuilder() + { + return dbProvider.CreateCommandBuilder(); + } + + public IDbConnection CreateConnection() + { + return dbProvider.CreateConnection(); + } + + public IDbDataParameter CreateParameter() + { + return dbProvider.CreateParameter(); + } + + public void Shutdown() + { + // no-op + } + + public string ConnectionString + { + get { return dbProvider.ConnectionString; } + set { dbProvider.ConnectionString = value; } + } + + public DbMetadata Metadata + { + get { throw new System.NotImplementedException(); } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj b/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj index b0a14846..94219426 100644 --- a/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj +++ b/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj @@ -43,6 +43,7 @@ ..\..\..\lib\net\2.0\Quartz.dll + @@ -54,6 +55,7 @@ + @@ -64,6 +66,7 @@ + @@ -73,6 +76,10 @@ {710961A3-0DF4-49E4-A26E-F5B9C044AC84} Spring.Core.2005 + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 +