001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.scheduling.quartz;
018:
019: import java.sql.Connection;
020: import java.sql.SQLException;
021:
022: import javax.sql.DataSource;
023:
024: import org.quartz.SchedulerConfigException;
025: import org.quartz.impl.jdbcjobstore.JobStoreCMT;
026: import org.quartz.spi.ClassLoadHelper;
027: import org.quartz.spi.SchedulerSignaler;
028: import org.quartz.utils.ConnectionProvider;
029: import org.quartz.utils.DBConnectionManager;
030:
031: import org.springframework.jdbc.datasource.DataSourceUtils;
032:
033: /**
034: * Subclass of Quartz's JobStoreCMT class that delegates to a Spring-managed
035: * DataSource instead of using a Quartz-managed connection pool. This JobStore
036: * will be used if SchedulerFactoryBean's "dataSource" property is set.
037: *
038: * <p>Supports both transactional and non-transactional DataSource access.
039: * With a non-XA DataSource and local Spring transactions, a single DataSource
040: * argument is sufficient. In case of an XA DataSource and global JTA transactions,
041: * SchedulerFactoryBean's "nonTransactionalDataSource" property should be set,
042: * passing in a non-XA DataSource that will not participate in global transactions.
043: *
044: * <p>Operations performed by this JobStore will properly participate in any
045: * kind of Spring-managed transaction, as it uses Spring's DataSourceUtils
046: * connection handling methods that are aware of a current transaction.
047: *
048: * <p>Note that all Quartz Scheduler operations that affect the persistent
049: * job store should usually be performed within active transactions,
050: * as they assume to get proper locks etc.
051: *
052: * @author Juergen Hoeller
053: * @since 1.1
054: * @see SchedulerFactoryBean#setDataSource
055: * @see SchedulerFactoryBean#setNonTransactionalDataSource
056: * @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
057: * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
058: */
059: public class LocalDataSourceJobStore extends JobStoreCMT {
060:
061: /**
062: * Name used for the transactional ConnectionProvider for Quartz.
063: * This provider will delegate to the local Spring-managed DataSource.
064: * @see org.quartz.utils.DBConnectionManager#addConnectionProvider
065: * @see SchedulerFactoryBean#setDataSource
066: */
067: public static final String TX_DATA_SOURCE_PREFIX = "springTxDataSource.";
068:
069: /**
070: * Name used for the non-transactional ConnectionProvider for Quartz.
071: * This provider will delegate to the local Spring-managed DataSource.
072: * @see org.quartz.utils.DBConnectionManager#addConnectionProvider
073: * @see SchedulerFactoryBean#setDataSource
074: */
075: public static final String NON_TX_DATA_SOURCE_PREFIX = "springNonTxDataSource.";
076:
077: private DataSource dataSource;
078:
079: public void initialize(ClassLoadHelper loadHelper,
080: SchedulerSignaler signaler) throws SchedulerConfigException {
081:
082: // Absolutely needs thread-bound DataSource to initialize.
083: this .dataSource = SchedulerFactoryBean
084: .getConfigTimeDataSource();
085: if (this .dataSource == null) {
086: throw new SchedulerConfigException(
087: "No local DataSource found for configuration - "
088: + "'dataSource' property must be set on SchedulerFactoryBean");
089: }
090:
091: // Configure transactional connection settings for Quartz.
092: setDataSource(TX_DATA_SOURCE_PREFIX + getInstanceName());
093: setDontSetAutoCommitFalse(true);
094:
095: // Register transactional ConnectionProvider for Quartz.
096: DBConnectionManager.getInstance().addConnectionProvider(
097: TX_DATA_SOURCE_PREFIX + getInstanceName(),
098: new ConnectionProvider() {
099: public Connection getConnection()
100: throws SQLException {
101: // Return a transactional Connection, if any.
102: return DataSourceUtils
103: .doGetConnection(dataSource);
104: }
105:
106: public void shutdown() {
107: // Do nothing - a Spring-managed DataSource has its own lifecycle.
108: }
109: });
110:
111: // Non-transactional DataSource is optional: fall back to default
112: // DataSource if not explicitly specified.
113: DataSource nonTxDataSource = SchedulerFactoryBean
114: .getConfigTimeNonTransactionalDataSource();
115: final DataSource nonTxDataSourceToUse = (nonTxDataSource != null ? nonTxDataSource
116: : this .dataSource);
117:
118: // Configure non-transactional connection settings for Quartz.
119: setNonManagedTXDataSource(NON_TX_DATA_SOURCE_PREFIX
120: + getInstanceName());
121:
122: // Register non-transactional ConnectionProvider for Quartz.
123: DBConnectionManager.getInstance().addConnectionProvider(
124: NON_TX_DATA_SOURCE_PREFIX + getInstanceName(),
125: new ConnectionProvider() {
126: public Connection getConnection()
127: throws SQLException {
128: // Always return a non-transactional Connection.
129: return nonTxDataSourceToUse.getConnection();
130: }
131:
132: public void shutdown() {
133: // Do nothing - a Spring-managed DataSource has its own lifecycle.
134: }
135: });
136:
137: super .initialize(loadHelper, signaler);
138: }
139:
140: protected void closeConnection(Connection con) {
141: // Will work for transactional and non-transactional connections.
142: DataSourceUtils.releaseConnection(con, this.dataSource);
143: }
144:
145: }
|