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.transaction.support;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: import org.springframework.beans.factory.InitializingBean;
023: import org.springframework.transaction.PlatformTransactionManager;
024: import org.springframework.transaction.TransactionDefinition;
025: import org.springframework.transaction.TransactionException;
026: import org.springframework.transaction.TransactionStatus;
027: import org.springframework.transaction.TransactionSystemException;
028:
029: /**
030: * Template class that simplifies programmatic transaction demarcation and
031: * transaction exception handling.
032: *
033: * <p>The central method is {@link #execute}, supporting transactional code that
034: * implements the {@link TransactionCallback} interface. This template handles
035: * the transaction lifecycle and possible exceptions such that neither the
036: * TransactionCallback implementation nor the calling code needs to explicitly
037: * handle transactions.
038: *
039: * <p>Typical usage: Allows for writing low-level data access objects that use
040: * resources such as JDBC DataSources but are not transaction-aware themselves.
041: * Instead, they can implicitly participate in transactions handled by higher-level
042: * application services utilizing this class, making calls to the low-level
043: * services via an inner-class callback object.
044: *
045: * <p>Can be used within a service implementation via direct instantiation with
046: * a transaction manager reference, or get prepared in an application context
047: * and passed to services as bean reference. Note: The transaction manager should
048: * always be configured as bean in the application context: in the first case given
049: * to the service directly, in the second case given to the prepared template.
050: *
051: * <p>Supports setting the propagation behavior and the isolation level by name,
052: * for convenient configuration in context definitions.
053: *
054: * @author Juergen Hoeller
055: * @since 17.03.2003
056: * @see #execute
057: * @see #setTransactionManager
058: * @see org.springframework.transaction.PlatformTransactionManager
059: */
060: public class TransactionTemplate extends DefaultTransactionDefinition
061: implements TransactionOperations, InitializingBean {
062:
063: /** Logger available to subclasses */
064: protected final Log logger = LogFactory.getLog(getClass());
065:
066: private PlatformTransactionManager transactionManager;
067:
068: /**
069: * Construct a new TransactionTemplate for bean usage.
070: * <p>Note: The PlatformTransactionManager needs to be set before
071: * any <code>execute</code> calls.
072: * @see #setTransactionManager
073: */
074: public TransactionTemplate() {
075: }
076:
077: /**
078: * Construct a new TransactionTemplate using the given transaction manager.
079: * @param transactionManager the transaction management strategy to be used
080: */
081: public TransactionTemplate(
082: PlatformTransactionManager transactionManager) {
083: this .transactionManager = transactionManager;
084: }
085:
086: /**
087: * Construct a new TransactionTemplate using the given transaction manager,
088: * taking its default settings from the given transaction definition.
089: * @param transactionManager the transaction management strategy to be used
090: * @param transactionDefinition the transaction definition to copy the
091: * default settings from. Local properties can still be set to change values.
092: */
093: public TransactionTemplate(
094: PlatformTransactionManager transactionManager,
095: TransactionDefinition transactionDefinition) {
096: super (transactionDefinition);
097: this .transactionManager = transactionManager;
098: }
099:
100: /**
101: * Set the transaction management strategy to be used.
102: */
103: public void setTransactionManager(
104: PlatformTransactionManager transactionManager) {
105: this .transactionManager = transactionManager;
106: }
107:
108: /**
109: * Return the transaction management strategy to be used.
110: */
111: public PlatformTransactionManager getTransactionManager() {
112: return this .transactionManager;
113: }
114:
115: public void afterPropertiesSet() {
116: if (this .transactionManager == null) {
117: throw new IllegalArgumentException(
118: "Property 'transactionManager' is required");
119: }
120: }
121:
122: public Object execute(TransactionCallback action)
123: throws TransactionException {
124: if (this .transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
125: return ((CallbackPreferringPlatformTransactionManager) this .transactionManager)
126: .execute(this , action);
127: } else {
128: TransactionStatus status = this .transactionManager
129: .getTransaction(this );
130: Object result = null;
131: try {
132: result = action.doInTransaction(status);
133: } catch (RuntimeException ex) {
134: // Transactional code threw application exception -> rollback
135: rollbackOnException(status, ex);
136: throw ex;
137: } catch (Error err) {
138: // Transactional code threw error -> rollback
139: rollbackOnException(status, err);
140: throw err;
141: }
142: this .transactionManager.commit(status);
143: return result;
144: }
145: }
146:
147: /**
148: * Perform a rollback, handling rollback exceptions properly.
149: * @param status object representing the transaction
150: * @param ex the thrown application exception or error
151: * @throws TransactionException in case of a rollback error
152: */
153: private void rollbackOnException(TransactionStatus status,
154: Throwable ex) throws TransactionException {
155: logger
156: .debug(
157: "Initiating transaction rollback on application exception",
158: ex);
159: try {
160: this .transactionManager.rollback(status);
161: } catch (TransactionSystemException ex2) {
162: logger
163: .error(
164: "Application exception overridden by rollback exception",
165: ex);
166: ex2.initApplicationException(ex);
167: throw ex2;
168: } catch (RuntimeException ex2) {
169: logger
170: .error(
171: "Application exception overridden by rollback exception",
172: ex);
173: throw ex2;
174: } catch (Error err) {
175: logger
176: .error(
177: "Application exception overridden by rollback error",
178: ex);
179: throw err;
180: }
181: }
182:
183: }
|