1 /*
2 * Copyright 2002-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.transaction.interceptor;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.util.Properties;
24
25 import org.aopalliance.intercept.MethodInterceptor;
26 import org.aopalliance.intercept.MethodInvocation;
27
28 import org.springframework.transaction.PlatformTransactionManager;
29 import org.springframework.transaction.TransactionStatus;
30 import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager;
31 import org.springframework.transaction.support.TransactionCallback;
32
33 /**
34 * AOP Alliance MethodInterceptor for declarative transaction
35 * management using the common Spring transaction infrastructure
36 * ({@link org.springframework.transaction.PlatformTransactionManager}).
37 *
38 * <p>Derives from the {@link TransactionAspectSupport} class which
39 * contains the integration with Spring's underlying transaction API.
40 * TransactionInterceptor simply calls the relevant superclass methods
41 * such as {@link #createTransactionIfNecessary} in the correct order.
42 *
43 * <p>TransactionInterceptors are thread-safe.
44 *
45 * @author Rod Johnson
46 * @author Juergen Hoeller
47 * @see TransactionProxyFactoryBean
48 * @see org.springframework.aop.framework.ProxyFactoryBean
49 * @see org.springframework.aop.framework.ProxyFactory
50 */
51 public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
52
53 /**
54 * Create a new TransactionInterceptor.
55 * <p>Transaction manager and transaction attributes still need to be set.
56 * @see #setTransactionManager
57 * @see #setTransactionAttributes(java.util.Properties)
58 * @see #setTransactionAttributeSource(TransactionAttributeSource)
59 */
60 public TransactionInterceptor() {
61 }
62
63 /**
64 * Create a new TransactionInterceptor.
65 * @param ptm the transaction manager to perform the actual transaction management
66 * @param attributes the transaction attributes in properties format
67 * @see #setTransactionManager
68 * @see #setTransactionAttributes(java.util.Properties)
69 */
70 public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
71 setTransactionManager(ptm);
72 setTransactionAttributes(attributes);
73 }
74
75 /**
76 * Create a new TransactionInterceptor.
77 * @param ptm the transaction manager to perform the actual transaction management
78 * @param tas the attribute source to be used to find transaction attributes
79 * @see #setTransactionManager
80 * @see #setTransactionAttributeSource(TransactionAttributeSource)
81 */
82 public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
83 setTransactionManager(ptm);
84 setTransactionAttributeSource(tas);
85 }
86
87
88 public Object invoke(final MethodInvocation invocation) throws Throwable {
89 // Work out the target class: may be <code>null</code>.
90 // The TransactionAttributeSource should be passed the target class
91 // as well as the method, which may be from an interface.
92 Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);
93
94 // If the transaction attribute is null, the method is non-transactional.
95 final TransactionAttribute txAttr =
96 getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
97 final String joinpointIdentification = methodIdentification(invocation.getMethod());
98
99 if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
100 // Standard transaction demarcation with getTransaction and commit/rollback calls.
101 TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
102 Object retVal = null;
103 try {
104 // This is an around advice: Invoke the next interceptor in the chain.
105 // This will normally result in a target object being invoked.
106 retVal = invocation.proceed();
107 }
108 catch (Throwable ex) {
109 // target invocation exception
110 completeTransactionAfterThrowing(txInfo, ex);
111 throw ex;
112 }
113 finally {
114 cleanupTransactionInfo(txInfo);
115 }
116 commitTransactionAfterReturning(txInfo);
117 return retVal;
118 }
119
120 else {
121 // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
122 try {
123 Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
124 new TransactionCallback() {
125 public Object doInTransaction(TransactionStatus status) {
126 TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
127 try {
128 return invocation.proceed();
129 }
130 catch (Throwable ex) {
131 if (txAttr.rollbackOn(ex)) {
132 // A RuntimeException: will lead to a rollback.
133 if (ex instanceof RuntimeException) {
134 throw (RuntimeException) ex;
135 }
136 else {
137 throw new ThrowableHolderException(ex);
138 }
139 }
140 else {
141 // A normal return value: will lead to a commit.
142 return new ThrowableHolder(ex);
143 }
144 }
145 finally {
146 cleanupTransactionInfo(txInfo);
147 }
148 }
149 });
150
151 // Check result: It might indicate a Throwable to rethrow.
152 if (result instanceof ThrowableHolder) {
153 throw ((ThrowableHolder) result).getThrowable();
154 }
155 else {
156 return result;
157 }
158 }
159 catch (ThrowableHolderException ex) {
160 throw ex.getCause();
161 }
162 }
163 }
164
165
166 //---------------------------------------------------------------------
167 // Serialization support
168 //---------------------------------------------------------------------
169
170 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
171 // Rely on default serialization, although this class itself doesn't carry state anyway...
172 ois.defaultReadObject();
173
174 // Serialize all relevant superclass fields.
175 // Superclass can't implement Serializable because it also serves as base class
176 // for AspectJ aspects (which are not allowed to implement Serializable)!
177 setTransactionManager((PlatformTransactionManager) ois.readObject());
178 setTransactionAttributeSource((TransactionAttributeSource) ois.readObject());
179 }
180
181 private void writeObject(ObjectOutputStream oos) throws IOException {
182 // Rely on default serialization, although this class itself doesn't carry state anyway...
183 oos.defaultWriteObject();
184
185 // Deserialize superclass fields.
186 oos.writeObject(getTransactionManager());
187 oos.writeObject(getTransactionAttributeSource());
188 }
189
190
191 /**
192 * Internal holder class for a Throwable, used as a return value
193 * from a TransactionCallback (to be subsequently unwrapped again).
194 */
195 private static class ThrowableHolder {
196
197 private final Throwable throwable;
198
199 public ThrowableHolder(Throwable throwable) {
200 this.throwable = throwable;
201 }
202
203 public final Throwable getThrowable() {
204 return this.throwable;
205 }
206 }
207
208
209 /**
210 * Internal holder class for a Throwable, used as a RuntimeException to be
211 * thrown from a TransactionCallback (and subsequently unwrapped again).
212 */
213 private static class ThrowableHolderException extends RuntimeException {
214
215 public ThrowableHolderException(Throwable throwable) {
216 super(throwable);
217 }
218
219 public String toString() {
220 return getCause().toString();
221 }
222 }
223
224 }