001: /*
002: * Copyright 2002-2006 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.interceptor;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.ObjectOutputStream;
022: import java.io.Serializable;
023: import java.util.Properties;
024:
025: import org.aopalliance.intercept.MethodInterceptor;
026: import org.aopalliance.intercept.MethodInvocation;
027:
028: import org.springframework.transaction.PlatformTransactionManager;
029: import org.springframework.transaction.TransactionStatus;
030: import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager;
031: import org.springframework.transaction.support.TransactionCallback;
032:
033: /**
034: * AOP Alliance MethodInterceptor providing declarative transaction
035: * management using the common Spring transaction infrastructure.
036: *
037: * <p>Derives from the TransactionAspectSupport class. That class contains
038: * the necessary calls into Spring's underlying transaction API:
039: * subclasses such as this are responsible for calling superclass methods
040: * such as <code>createTransactionIfNecessary</code> in the correct order,
041: * in the event of normal invocation return or an exception.
042: *
043: * <p>TransactionInterceptors are thread-safe.
044: *
045: * @author Rod Johnson
046: * @author Juergen Hoeller
047: * @see TransactionProxyFactoryBean
048: * @see org.springframework.aop.framework.ProxyFactoryBean
049: * @see org.springframework.transaction.interceptor.TransactionAspectSupport
050: * @see org.springframework.transaction.PlatformTransactionManager
051: */
052: public class TransactionInterceptor extends TransactionAspectSupport
053: implements MethodInterceptor, Serializable {
054:
055: /**
056: * Create a new TransactionInterceptor.
057: * Transaction manager and transaction attributes still need to be set.
058: * @see #setTransactionManager
059: * @see #setTransactionAttributes(java.util.Properties)
060: * @see #setTransactionAttributeSource(TransactionAttributeSource)
061: */
062: public TransactionInterceptor() {
063: }
064:
065: /**
066: * Create a new TransactionInterceptor.
067: * @param ptm the transaction manager to perform the actual transaction management
068: * @param attributes the transaction attributes in properties format
069: * @see #setTransactionManager
070: * @see #setTransactionAttributes(java.util.Properties)
071: */
072: public TransactionInterceptor(PlatformTransactionManager ptm,
073: Properties attributes) {
074: setTransactionManager(ptm);
075: setTransactionAttributes(attributes);
076: }
077:
078: /**
079: * Create a new TransactionInterceptor.
080: * @param ptm the transaction manager to perform the actual transaction management
081: * @param tas the attribute source to be used to find transaction attributes
082: * @see #setTransactionManager
083: * @see #setTransactionAttributeSource(TransactionAttributeSource)
084: */
085: public TransactionInterceptor(PlatformTransactionManager ptm,
086: TransactionAttributeSource tas) {
087: setTransactionManager(ptm);
088: setTransactionAttributeSource(tas);
089: }
090:
091: public Object invoke(final MethodInvocation invocation)
092: throws Throwable {
093: // Work out the target class: may be <code>null</code>.
094: // The TransactionAttributeSource should be passed the target class
095: // as well as the method, which may be from an interface.
096: Class targetClass = (invocation.getThis() != null ? invocation
097: .getThis().getClass() : null);
098:
099: // If the transaction attribute is null, the method is non-transactional.
100: final TransactionAttribute txAttr = getTransactionAttributeSource()
101: .getTransactionAttribute(invocation.getMethod(),
102: targetClass);
103: final String joinpointIdentification = methodIdentification(invocation
104: .getMethod());
105:
106: if (txAttr == null
107: || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
108: // Standard transaction demarcation with getTransaction and commit/rollback calls.
109: TransactionInfo txInfo = createTransactionIfNecessary(
110: txAttr, joinpointIdentification);
111: Object retVal = null;
112: try {
113: // This is an around advice: Invoke the next interceptor in the chain.
114: // This will normally result in a target object being invoked.
115: retVal = invocation.proceed();
116: } catch (Throwable ex) {
117: // target invocation exception
118: completeTransactionAfterThrowing(txInfo, ex);
119: throw ex;
120: } finally {
121: cleanupTransactionInfo(txInfo);
122: }
123: commitTransactionAfterReturning(txInfo);
124: return retVal;
125: }
126:
127: else {
128: // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
129: try {
130: Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager())
131: .execute(txAttr, new TransactionCallback() {
132: public Object doInTransaction(
133: TransactionStatus status) {
134: TransactionInfo txInfo = prepareTransactionInfo(
135: txAttr,
136: joinpointIdentification, status);
137: try {
138: return invocation.proceed();
139: } catch (Throwable ex) {
140: if (txAttr.rollbackOn(ex)) {
141: // A RuntimeException: will lead to a rollback.
142: throw new ThrowableHolderException(
143: ex);
144: } else {
145: // A normal return value: will lead to a commit.
146: return new ThrowableHolder(ex);
147: }
148: } finally {
149: cleanupTransactionInfo(txInfo);
150: }
151: }
152: });
153:
154: // Check result: It might indicate a Throwable to rethrow.
155: if (result instanceof ThrowableHolder) {
156: throw ((ThrowableHolder) result).getThrowable();
157: } else {
158: return result;
159: }
160: } catch (ThrowableHolderException ex) {
161: throw ex.getThrowable();
162: }
163: }
164: }
165:
166: //---------------------------------------------------------------------
167: // Serialization support
168: //---------------------------------------------------------------------
169:
170: private void readObject(ObjectInputStream ois) throws IOException,
171: ClassNotFoundException {
172: // Rely on default serialization, although this class itself doesn't carry state anyway...
173: ois.defaultReadObject();
174:
175: // Serialize all relevant superclass fields.
176: // Superclass can't implement Serializable because it also serves as base class
177: // for AspectJ aspects (which are not allowed to implement Serializable)!
178: setTransactionManager((PlatformTransactionManager) ois
179: .readObject());
180: setTransactionAttributeSource((TransactionAttributeSource) ois
181: .readObject());
182: }
183:
184: private void writeObject(ObjectOutputStream oos) throws IOException {
185: // Rely on default serialization, although this class itself doesn't carry state anyway...
186: oos.defaultWriteObject();
187:
188: // Deserialize superclass fields.
189: oos.writeObject(getTransactionManager());
190: oos.writeObject(getTransactionAttributeSource());
191: }
192:
193: /**
194: * Internal holder class for a Throwable, used as a return value
195: * from a TransactionCallback (to be subsequently unwrapped again).
196: */
197: private static class ThrowableHolder {
198:
199: private final Throwable throwable;
200:
201: public ThrowableHolder(Throwable throwable) {
202: this .throwable = throwable;
203: }
204:
205: public Throwable getThrowable() {
206: return throwable;
207: }
208: }
209:
210: /**
211: * Internal holder class for a Throwable, used as a RuntimeException to be
212: * thrown from a TransactionCallback (and subsequently unwrapped again).
213: */
214: private static class ThrowableHolderException extends
215: RuntimeException {
216:
217: private final Throwable throwable;
218:
219: public ThrowableHolderException(Throwable throwable) {
220: super (throwable.toString());
221: this .throwable = throwable;
222: }
223:
224: public Throwable getThrowable() {
225: return throwable;
226: }
227:
228: public String toString() {
229: return this.throwable.toString();
230: }
231: }
232:
233: }
|