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