Open Source Repository

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



org/springframework/transaction/interceptor/RollbackRuleAttribute.java
/*
 * Copyright 2002-2007 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 org.springframework.util.Assert;

import java.io.Serializable;

/**
 * Rule determining whether or not a given exception (and any subclasses)
 * should cause a rollback.
 *
 <p>Multiple such rules can be applied to determine whether a transaction
 * should commit or rollback after an exception has been thrown.
 *
 @author Rod Johnson
 @since 09.04.2003
 @see NoRollbackRuleAttribute
 */
public class RollbackRuleAttribute implements Serializable{

  /**
   * The {@link RollbackRuleAttribute rollback rule} for
   {@link RuntimeException RuntimeExceptions}.
   */
  public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS =
      new RollbackRuleAttribute(RuntimeException.class);


  /**
   * Could hold exception, resolving class name but would always require FQN.
   * This way does multiple string comparisons, but how often do we decide
   * whether to roll back a transaction following an exception?
   */
  private final String exceptionName;


  /**
   * Create a new instance of the <code>RollbackRuleAttribute</code> class.
   <p>This is the preferred way to construct a rollback rule that matches
   * the supplied {@link Exception} class (and subclasses).
   @param clazz throwable class; must be {@link Throwable} or a subclass
   * of <code>Throwable</code>
   @throws IllegalArgumentException if the supplied <code>clazz</code> is
   * not a <code>Throwable</code> type or is <code>null</code>
   */
  public RollbackRuleAttribute(Class clazz) {
    Assert.notNull(clazz, "'clazz' cannot be null.");
    if (!Throwable.class.isAssignableFrom(clazz)) {
      throw new IllegalArgumentException(
          "Cannot construct rollback rule from [" + clazz.getName() "]: it's not a Throwable");
    }
    this.exceptionName = clazz.getName();
  }

  /**
   * Create a new instance of the <code>RollbackRuleAttribute</code> class
   * for the given <code>exceptionName</code>.
   <p>This can be a substring, with no wildcard support at present. A value
   * of "ServletException" would match
   <code>javax.servlet.ServletException</code> and subclasses, for example.
   <p><b>NB:</b> Consider carefully how specific the pattern is, and
   * whether to include package information (which is not mandatory). For
   * example, "Exception" will match nearly anything, and will probably hide
   * other rules. "java.lang.Exception" would be correct if "Exception" was
   * meant to define a rule for all checked exceptions. With more unusual
   * exception names such as "BaseBusinessException" there's no need to use a
   * fully package-qualified name.
   @param exceptionName the exception name pattern; can also be a fully
   * package-qualified class name
   @throws IllegalArgumentException if the supplied
   <code>exceptionName</code> is <code>null</code> or empty
   */
  public RollbackRuleAttribute(String exceptionName) {
    Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty.");
    this.exceptionName = exceptionName;
  }


  /**
   * Return the pattern for the exception name.
   */
  public String getExceptionName() {
    return exceptionName;
  }

  /**
   * Return the depth of the superclass matching.
   <p><code>0</code> means <code>ex</code> matches exactly. Returns
   <code>-1</code> if there is no match. Otherwise, returns depth with the
   * lowest depth winning.
   */
  public int getDepth(Throwable ex) {
    return getDepth(ex.getClass()0);
  }


  private int getDepth(Class exceptionClass, int depth) {
    if (exceptionClass.getName().indexOf(this.exceptionName!= -1) {
      // Found it!
      return depth;
    }
    // If we've gone as far as we can go and haven't found it...
    if (exceptionClass.equals(Throwable.class)) {
      return -1;
    }
    return getDepth(exceptionClass.getSuperclass(), depth + 1);
  }


  @Override
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }
    if (!(other instanceof RollbackRuleAttribute)) {
      return false;
    }
    RollbackRuleAttribute rhs = (RollbackRuleAttributeother;
    return this.exceptionName.equals(rhs.exceptionName);
  }
  
  @Override
  public int hashCode() {
    return this.exceptionName.hashCode();
  }

  @Override
  public String toString() {
    return "RollbackRuleAttribute with pattern [" this.exceptionName + "]";
  }

}