/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.interceptor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Properties;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager;
import org.springframework.transaction.support.TransactionCallback;
/**
* AOP Alliance MethodInterceptor for declarative transaction
* management using the common Spring transaction infrastructure
* ({@link org.springframework.transaction.PlatformTransactionManager}).
*
* <p>Derives from the {@link TransactionAspectSupport} class which
* contains the integration with Spring's underlying transaction API.
* TransactionInterceptor simply calls the relevant superclass methods
* such as {@link #createTransactionIfNecessary} in the correct order.
*
* <p>TransactionInterceptors are thread-safe.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see TransactionProxyFactoryBean
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see org.springframework.aop.framework.ProxyFactory
*/
@SuppressWarnings("serial")
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
/**
* Create a new TransactionInterceptor.
* <p>Transaction manager and transaction attributes still need to be set.
* @see #setTransactionManager
* @see #setTransactionAttributes(java.util.Properties)
* @see #setTransactionAttributeSource(TransactionAttributeSource)
*/
public TransactionInterceptor() {
}
/**
* Create a new TransactionInterceptor.
* @param ptm the transaction manager to perform the actual transaction management
* @param attributes the transaction attributes in properties format
* @see #setTransactionManager
* @see #setTransactionAttributes(java.util.Properties)
*/
public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
setTransactionManager(ptm);
setTransactionAttributes(attributes);
}
/**
* Create a new TransactionInterceptor.
* @param ptm the transaction manager to perform the actual transaction management
* @param tas the attribute source to be used to find transaction attributes
* @see #setTransactionManager
* @see #setTransactionAttributeSource(TransactionAttributeSource)
*/
public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
setTransactionManager(ptm);
setTransactionAttributeSource(tas);
}
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be <code>null</code>.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceed();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void writeObject(ObjectOutputStream oos) throws IOException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
oos.defaultWriteObject();
// Deserialize superclass fields.
oos.writeObject(getTransactionManagerBeanName());
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
oos.writeObject(getBeanFactory());
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
ois.defaultReadObject();
// Serialize all relevant superclass fields.
// Superclass can't implement Serializable because it also serves as base class
// for AspectJ aspects (which are not allowed to implement Serializable)!
setTransactionManagerBeanName((String) ois.readObject());
setTransactionManager((PlatformTransactionManager) ois.readObject());
setTransactionAttributeSource((TransactionAttributeSource) ois.readObject());
setBeanFactory((BeanFactory) ois.readObject());
}
/**
* Internal holder class for a Throwable, used as a return value
* from a TransactionCallback (to be subsequently unwrapped again).
*/
private static class ThrowableHolder {
private final Throwable throwable;
public ThrowableHolder(Throwable throwable) {
this.throwable = throwable;
}
public final Throwable getThrowable() {
return this.throwable;
}
}
/**
* Internal holder class for a Throwable, used as a RuntimeException to be
* thrown from a TransactionCallback (and subsequently unwrapped again).
*/
private static class ThrowableHolderException extends RuntimeException {
public ThrowableHolderException(Throwable throwable) {
super(throwable);
}
@Override
public String toString() {
return getCause().toString();
}
}
}
|