001: /*
002: * Copyright 2004-2005 OpenSymphony
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy
006: * 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, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations
014: * under the License.
015: *
016: */
017:
018: /*
019: * Previously Copyright (c) 2001-2004 James House
020: */
021: package org.quartz.impl.jdbcjobstore;
022:
023: import java.sql.Connection;
024: import java.sql.SQLException;
025:
026: import org.quartz.JobPersistenceException;
027: import org.quartz.SchedulerConfigException;
028: import org.quartz.spi.ClassLoadHelper;
029: import org.quartz.spi.SchedulerSignaler;
030: import org.quartz.utils.DBConnectionManager;
031:
032: /**
033: * <p>
034: * <code>JobStoreCMT</code> is meant to be used in an application-server
035: * environment that provides container-managed-transactions. No commit /
036: * rollback will be1 handled by this class.
037: * </p>
038: *
039: * <p>
040: * If you need commit / rollback, use <code>{@link
041: * org.quartz.impl.jdbcjobstore.JobStoreTX}</code>
042: * instead.
043: * </p>
044: *
045: * @author <a href="mailto:jeff@binaryfeed.org">Jeffrey Wescott</a>
046: * @author James House
047: * @author Srinivas Venkatarangaiah
048: *
049: */
050: public class JobStoreCMT extends JobStoreSupport {
051:
052: /*
053: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
054: *
055: * Data members.
056: *
057: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
058: */
059:
060: protected String nonManagedTxDsName;
061:
062: // Great name huh?
063: protected boolean dontSetNonManagedTXConnectionAutoCommitFalse = false;
064:
065: protected boolean setTxIsolationLevelReadCommitted = false;
066:
067: /*
068: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
069: *
070: * Interface.
071: *
072: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
073: */
074:
075: /**
076: * <p>
077: * Set the name of the <code>DataSource</code> that should be used for
078: * performing database functions.
079: * </p>
080: */
081: public void setNonManagedTXDataSource(String nonManagedTxDsName) {
082: this .nonManagedTxDsName = nonManagedTxDsName;
083: }
084:
085: /**
086: * <p>
087: * Get the name of the <code>DataSource</code> that should be used for
088: * performing database functions.
089: * </p>
090: */
091: public String getNonManagedTXDataSource() {
092: return nonManagedTxDsName;
093: }
094:
095: public boolean isDontSetNonManagedTXConnectionAutoCommitFalse() {
096: return dontSetNonManagedTXConnectionAutoCommitFalse;
097: }
098:
099: /**
100: * Don't call set autocommit(false) on connections obtained from the
101: * DataSource. This can be helpfull in a few situations, such as if you
102: * have a driver that complains if it is called when it is already off.
103: *
104: * @param b
105: */
106: public void setDontSetNonManagedTXConnectionAutoCommitFalse(
107: boolean b) {
108: dontSetNonManagedTXConnectionAutoCommitFalse = b;
109: }
110:
111: public boolean isTxIsolationLevelReadCommitted() {
112: return setTxIsolationLevelReadCommitted;
113: }
114:
115: /**
116: * Set the transaction isolation level of DB connections to sequential.
117: *
118: * @param b
119: */
120: public void setTxIsolationLevelReadCommitted(boolean b) {
121: setTxIsolationLevelReadCommitted = b;
122: }
123:
124: public void initialize(ClassLoadHelper loadHelper,
125: SchedulerSignaler signaler) throws SchedulerConfigException {
126:
127: if (nonManagedTxDsName == null) {
128: throw new SchedulerConfigException(
129: "Non-ManagedTX DataSource name not set! "
130: + "If your 'org.quartz.jobStore.dataSource' is XA, then set "
131: + "'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA "
132: + "datasource (for the same DB). "
133: + "Otherwise, you can set them to be the same.");
134: }
135:
136: if (getLockHandler() == null) {
137: // If the user hasn't specified an explicit lock handler,
138: // then we *must* use DB locks with CMT...
139: setUseDBLocks(true);
140: }
141:
142: super .initialize(loadHelper, signaler);
143:
144: getLog().info("JobStoreCMT initialized.");
145: }
146:
147: public void shutdown() {
148:
149: super .shutdown();
150:
151: try {
152: DBConnectionManager.getInstance().shutdown(
153: getNonManagedTXDataSource());
154: } catch (SQLException sqle) {
155: getLog().warn("Database connection shutdown unsuccessful.",
156: sqle);
157: }
158: }
159:
160: protected Connection getNonManagedTXConnection()
161: throws JobPersistenceException {
162: Connection conn = null;
163: try {
164: conn = DBConnectionManager.getInstance().getConnection(
165: getNonManagedTXDataSource());
166: } catch (SQLException sqle) {
167: throw new JobPersistenceException(
168: "Failed to obtain DB connection from data source '"
169: + getNonManagedTXDataSource() + "': "
170: + sqle.toString(), sqle);
171: } catch (Throwable e) {
172: throw new JobPersistenceException(
173: "Failed to obtain DB connection from data source '"
174: + getNonManagedTXDataSource() + "': "
175: + e.toString(),
176: e,
177: JobPersistenceException.ERR_PERSISTENCE_CRITICAL_FAILURE);
178: }
179:
180: if (conn == null) {
181: throw new JobPersistenceException(
182: "Could not get connection from DataSource '"
183: + getNonManagedTXDataSource() + "'");
184: }
185:
186: // Protect connection attributes we might change.
187: conn = getAttributeRestoringConnection(conn);
188:
189: // Set any connection connection attributes we are to override.
190: try {
191: if (!isDontSetNonManagedTXConnectionAutoCommitFalse()) {
192: conn.setAutoCommit(false);
193: }
194:
195: if (isTxIsolationLevelReadCommitted()) {
196: conn
197: .setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
198: }
199: } catch (SQLException sqle) {
200: getLog()
201: .warn(
202: "Failed to override connection auto commit/transaction isolation.",
203: sqle);
204: } catch (Throwable e) {
205: try {
206: conn.close();
207: } catch (Throwable tt) {
208: }
209:
210: throw new JobPersistenceException(
211: "Failure setting up connection.", e);
212: }
213:
214: return conn;
215: }
216:
217: /**
218: * Execute the given callback having optionally aquired the given lock.
219: * Because CMT assumes that the connection is already part of a managed
220: * transaction, it does not attempt to commit or rollback the
221: * enclosing transaction.
222: *
223: * @param lockName The name of the lock to aquire, for example
224: * "TRIGGER_ACCESS". If null, then no lock is aquired, but the
225: * txCallback is still executed in a transaction.
226: *
227: * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback)
228: * @see JobStoreTX#executeInLock(String, TransactionCallback)
229: * @see JobStoreSupport#getNonManagedTXConnection()
230: * @see JobStoreSupport#getConnection()
231: */
232: protected Object executeInLock(String lockName,
233: TransactionCallback txCallback)
234: throws JobPersistenceException {
235: boolean transOwner = false;
236: Connection conn = null;
237: try {
238: if (lockName != null) {
239: // If we aren't using db locks, then delay getting DB connection
240: // until after aquiring the lock since it isn't needed.
241: if (getLockHandler().requiresConnection()) {
242: conn = getConnection();
243: }
244:
245: transOwner = getLockHandler()
246: .obtainLock(conn, lockName);
247: }
248:
249: if (conn == null) {
250: conn = getConnection();
251: }
252:
253: return txCallback.execute(conn);
254: } finally {
255: try {
256: releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner);
257: } finally {
258: cleanupConnection(conn);
259: }
260: }
261: }
262: }
263:
264: // EOF
|