Open Source Repository

Home /spring/spring-transaction-3.0.5 | Repository Home


org/springframework/transaction/interceptor/TransactionInterceptor.java
/*
 * 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 invocationthrows 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 = ((CallbackPreferringPlatformTransactionManagertm).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 (RuntimeExceptionex;
                    }
                    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 ((ThrowableHolderresult).getThrowable();
        }
        else {
          return result;
        }
      }
      catch (ThrowableHolderException ex) {
        throw ex.getCause();
      }
    }
  }


  //---------------------------------------------------------------------
  // Serialization support
  //---------------------------------------------------------------------

  private void writeObject(ObjectOutputStream oosthrows 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 oisthrows 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((Stringois.readObject());
    setTransactionManager((PlatformTransactionManagerois.readObject());
    setTransactionAttributeSource((TransactionAttributeSourceois.readObject());
    setBeanFactory((BeanFactoryois.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();
    }
  }

}