/*
* 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 = (RollbackRuleAttribute) other;
return this.exceptionName.equals(rhs.exceptionName);
}
@Override
public int hashCode() {
return this.exceptionName.hashCode();
}
@Override
public String toString() {
return "RollbackRuleAttribute with pattern [" + this.exceptionName + "]";
}
}
|