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.jca.cci.connection;
018:
019: import javax.resource.NotSupportedException;
020: import javax.resource.ResourceException;
021: import javax.resource.cci.Connection;
022: import javax.resource.cci.ConnectionFactory;
023: import javax.resource.spi.LocalTransactionException;
024:
025: import org.springframework.beans.factory.InitializingBean;
026: import org.springframework.transaction.CannotCreateTransactionException;
027: import org.springframework.transaction.TransactionDefinition;
028: import org.springframework.transaction.TransactionException;
029: import org.springframework.transaction.TransactionSystemException;
030: import org.springframework.transaction.support.AbstractPlatformTransactionManager;
031: import org.springframework.transaction.support.DefaultTransactionStatus;
032: import org.springframework.transaction.support.TransactionSynchronizationManager;
033: import org.springframework.transaction.support.ResourceTransactionManager;
034:
035: /**
036: * {@link org.springframework.transaction.PlatformTransactionManager} implementation
037: * that manages local transactions for a single CCI ConnectionFactory.
038: * Binds a CCI Connection from the specified ConnectionFactory to the thread,
039: * potentially allowing for one thread-bound Connection per ConnectionFactory.
040: *
041: * <p>Application code is required to retrieve the CCI Connection via
042: * {@link ConnectionFactoryUtils#getConnection(ConnectionFactory)} instead of a standard
043: * J2EE-style {@link ConnectionFactory#getConnection()} call. Spring classes such as
044: * {@link org.springframework.jca.cci.core.CciTemplate} use this strategy implicitly.
045: * If not used in combination with this transaction manager, the
046: * {@link ConnectionFactoryUtils} lookup strategy behaves exactly like the native
047: * DataSource lookup; it can thus be used in a portable fashion.
048: *
049: * <p>Alternatively, you can allow application code to work with the standard
050: * J2EE lookup pattern {@link ConnectionFactory#getConnection()}, for example
051: * for legacy code that is not aware of Spring at all. In that case, define a
052: * {@link TransactionAwareConnectionFactoryProxy} for your target ConnectionFactory,
053: * which will automatically participate in Spring-managed transactions.
054: *
055: * @author Thierry Templier
056: * @author Juergen Hoeller
057: * @since 1.2
058: * @see ConnectionFactoryUtils#getConnection(javax.resource.cci.ConnectionFactory)
059: * @see ConnectionFactoryUtils#releaseConnection
060: * @see TransactionAwareConnectionFactoryProxy
061: * @see org.springframework.jca.cci.core.CciTemplate
062: */
063: public class CciLocalTransactionManager extends
064: AbstractPlatformTransactionManager implements
065: ResourceTransactionManager, InitializingBean {
066:
067: private ConnectionFactory connectionFactory;
068:
069: /**
070: * Create a new CciLocalTransactionManager instance.
071: * A ConnectionFactory has to be set to be able to use it.
072: * @see #setConnectionFactory
073: */
074: public CciLocalTransactionManager() {
075: }
076:
077: /**
078: * Create a new CciLocalTransactionManager instance.
079: * @param connectionFactory CCI ConnectionFactory to manage local transactions for
080: */
081: public CciLocalTransactionManager(
082: ConnectionFactory connectionFactory) {
083: setConnectionFactory(connectionFactory);
084: afterPropertiesSet();
085: }
086:
087: /**
088: * Set the CCI ConnectionFactory that this instance should manage local
089: * transactions for.
090: */
091: public void setConnectionFactory(ConnectionFactory cf) {
092: if (cf instanceof TransactionAwareConnectionFactoryProxy) {
093: // If we got a TransactionAwareConnectionFactoryProxy, we need to perform transactions
094: // for its underlying target ConnectionFactory, else JMS access code won't see
095: // properly exposed transactions (i.e. transactions for the target ConnectionFactory).
096: this .connectionFactory = ((TransactionAwareConnectionFactoryProxy) cf)
097: .getTargetConnectionFactory();
098: } else {
099: this .connectionFactory = cf;
100: }
101: }
102:
103: /**
104: * Return the CCI ConnectionFactory that this instance manages local
105: * transactions for.
106: */
107: public ConnectionFactory getConnectionFactory() {
108: return this .connectionFactory;
109: }
110:
111: public void afterPropertiesSet() {
112: if (getConnectionFactory() == null) {
113: throw new IllegalArgumentException(
114: "Property 'connectionFactory' is required");
115: }
116: }
117:
118: public Object getResourceFactory() {
119: return getConnectionFactory();
120: }
121:
122: protected Object doGetTransaction() {
123: CciLocalTransactionObject txObject = new CciLocalTransactionObject();
124: ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
125: .getResource(getConnectionFactory());
126: txObject.setConnectionHolder(conHolder);
127: return txObject;
128: }
129:
130: protected boolean isExistingTransaction(Object transaction) {
131: CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
132: // Consider a pre-bound connection as transaction.
133: return (txObject.getConnectionHolder() != null);
134: }
135:
136: protected void doBegin(Object transaction,
137: TransactionDefinition definition) {
138: CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
139:
140: Connection con = null;
141:
142: try {
143: con = getConnectionFactory().getConnection();
144: if (logger.isDebugEnabled()) {
145: logger.debug("Acquired Connection [" + con
146: + "] for local CCI transaction");
147: }
148:
149: txObject.setConnectionHolder(new ConnectionHolder(con));
150: txObject.getConnectionHolder()
151: .setSynchronizedWithTransaction(true);
152:
153: con.getLocalTransaction().begin();
154: int timeout = determineTimeout(definition);
155: if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
156: txObject.getConnectionHolder().setTimeoutInSeconds(
157: timeout);
158: }
159: TransactionSynchronizationManager.bindResource(
160: getConnectionFactory(), txObject
161: .getConnectionHolder());
162: }
163:
164: catch (NotSupportedException ex) {
165: ConnectionFactoryUtils.releaseConnection(con,
166: getConnectionFactory());
167: throw new CannotCreateTransactionException(
168: "CCI Connection does not support local transactions",
169: ex);
170: } catch (LocalTransactionException ex) {
171: ConnectionFactoryUtils.releaseConnection(con,
172: getConnectionFactory());
173: throw new CannotCreateTransactionException(
174: "Could not begin local CCI transaction", ex);
175: } catch (ResourceException ex) {
176: ConnectionFactoryUtils.releaseConnection(con,
177: getConnectionFactory());
178: throw new TransactionSystemException(
179: "Unexpected failure on begin of CCI local transaction",
180: ex);
181: }
182: }
183:
184: protected Object doSuspend(Object transaction) {
185: CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
186: txObject.setConnectionHolder(null);
187: return TransactionSynchronizationManager
188: .unbindResource(getConnectionFactory());
189: }
190:
191: protected void doResume(Object transaction,
192: Object suspendedResources) {
193: ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;
194: TransactionSynchronizationManager.bindResource(
195: getConnectionFactory(), conHolder);
196: }
197:
198: protected boolean isRollbackOnly(Object transaction)
199: throws TransactionException {
200: CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
201: return txObject.getConnectionHolder().isRollbackOnly();
202: }
203:
204: protected void doCommit(DefaultTransactionStatus status) {
205: CciLocalTransactionObject txObject = (CciLocalTransactionObject) status
206: .getTransaction();
207: Connection con = txObject.getConnectionHolder().getConnection();
208: if (status.isDebug()) {
209: logger
210: .debug("Committing CCI local transaction on Connection ["
211: + con + "]");
212: }
213: try {
214: con.getLocalTransaction().commit();
215: } catch (LocalTransactionException ex) {
216: throw new TransactionSystemException(
217: "Could not commit CCI local transaction", ex);
218: } catch (ResourceException ex) {
219: throw new TransactionSystemException(
220: "Unexpected failure on commit of CCI local transaction",
221: ex);
222: }
223: }
224:
225: protected void doRollback(DefaultTransactionStatus status) {
226: CciLocalTransactionObject txObject = (CciLocalTransactionObject) status
227: .getTransaction();
228: Connection con = txObject.getConnectionHolder().getConnection();
229: if (status.isDebug()) {
230: logger
231: .debug("Rolling back CCI local transaction on Connection ["
232: + con + "]");
233: }
234: try {
235: con.getLocalTransaction().rollback();
236: } catch (LocalTransactionException ex) {
237: throw new TransactionSystemException(
238: "Could not roll back CCI local transaction", ex);
239: } catch (ResourceException ex) {
240: throw new TransactionSystemException(
241: "Unexpected failure on rollback of CCI local transaction",
242: ex);
243: }
244: }
245:
246: protected void doSetRollbackOnly(DefaultTransactionStatus status) {
247: CciLocalTransactionObject txObject = (CciLocalTransactionObject) status
248: .getTransaction();
249: if (status.isDebug()) {
250: logger.debug("Setting CCI local transaction ["
251: + txObject.getConnectionHolder().getConnection()
252: + "] rollback-only");
253: }
254: txObject.getConnectionHolder().setRollbackOnly();
255: }
256:
257: protected void doCleanupAfterCompletion(Object transaction) {
258: CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
259:
260: // Remove the connection holder from the thread.
261: TransactionSynchronizationManager
262: .unbindResource(getConnectionFactory());
263: txObject.getConnectionHolder().clear();
264:
265: Connection con = txObject.getConnectionHolder().getConnection();
266: if (logger.isDebugEnabled()) {
267: logger.debug("Releasing CCI Connection [" + con
268: + "] after transaction");
269: }
270: ConnectionFactoryUtils.releaseConnection(con,
271: getConnectionFactory());
272: }
273:
274: /**
275: * CCI local transaction object, representing a ConnectionHolder.
276: * Used as transaction object by CciLocalTransactionManager.
277: * @see ConnectionHolder
278: */
279: private static class CciLocalTransactionObject {
280:
281: private ConnectionHolder connectionHolder;
282:
283: public void setConnectionHolder(
284: ConnectionHolder connectionHolder) {
285: this .connectionHolder = connectionHolder;
286: }
287:
288: public ConnectionHolder getConnectionHolder() {
289: return connectionHolder;
290: }
291: }
292:
293: }
|