001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.transaction.interceptor;
018:
019: import org.springframework.util.Assert;
020:
021: import java.io.Serializable;
022:
023: /**
024: * Rule determining whether or not a given exception (and any subclasses)
025: * should cause a rollback.
026: *
027: * <p>Multiple such rules can be applied to determine whether a transaction
028: * should commit or rollback after an exception has been thrown.
029: *
030: * @author Rod Johnson
031: * @since 09.04.2003
032: * @see NoRollbackRuleAttribute
033: */
034: public class RollbackRuleAttribute implements Serializable {
035:
036: /**
037: * The {@link RollbackRuleAttribute rollback rule} for
038: * {@link RuntimeException RuntimeExceptions}.
039: */
040: public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS = new RollbackRuleAttribute(
041: RuntimeException.class);
042:
043: /**
044: * Could hold exception, resolving class name but would always require FQN.
045: * This way does multiple string comparisons, but how often do we decide
046: * whether to roll back a transaction following an exception?
047: */
048: private final String exceptionName;
049:
050: /**
051: * Create a new instance of the <code>RollbackRuleAttribute</code> class.
052: * <p>This is the preferred way to construct a rollback rule that matches
053: * the supplied {@link Exception} class (and subclasses).
054: * @param clazz throwable class; must be {@link Throwable} or a subclass
055: * of <code>Throwable</code>
056: * @throws IllegalArgumentException if the supplied <code>clazz</code> is
057: * not a <code>Throwable</code> type or is <code>null</code>
058: */
059: public RollbackRuleAttribute(Class clazz) {
060: Assert.notNull(clazz, "'clazz' cannot be null.");
061: if (!Throwable.class.isAssignableFrom(clazz)) {
062: throw new IllegalArgumentException(
063: "Cannot construct rollback rule from ["
064: + clazz.getName()
065: + "]: it's not a Throwable");
066: }
067: this .exceptionName = clazz.getName();
068: }
069:
070: /**
071: * Create a new instance of the <code>RollbackRuleAttribute</code> class
072: * for the given <code>exceptionName</code>.
073: * <p>This can be a substring, with no wildcard support at present. A value
074: * of "ServletException" would match
075: * <code>javax.servlet.ServletException</code> and subclasses, for example.
076: * <p><b>NB:</b> Consider carefully how specific the pattern is, and
077: * whether to include package information (which is not mandatory). For
078: * example, "Exception" will match nearly anything, and will probably hide
079: * other rules. "java.lang.Exception" would be correct if "Exception" was
080: * meant to define a rule for all checked exceptions. With more unusual
081: * exception names such as "BaseBusinessException" there's no need to use a
082: * fully package-qualified name.
083: * @param exceptionName the exception name pattern; can also be a fully
084: * package-qualified class name
085: * @throws IllegalArgumentException if the supplied
086: * <code>exceptionName</code> is <code>null</code> or empty
087: */
088: public RollbackRuleAttribute(String exceptionName) {
089: Assert.hasText(exceptionName,
090: "'exceptionName' cannot be null or empty.");
091: this .exceptionName = exceptionName;
092: }
093:
094: /**
095: * Return the pattern for the exception name.
096: */
097: public String getExceptionName() {
098: return exceptionName;
099: }
100:
101: /**
102: * Return the depth of the superclass matching.
103: * <p><code>0</code> means <code>ex</code> matches exactly. Returns
104: * <code>-1</code> if there is no match. Otherwise, returns depth with the
105: * lowest depth winning.
106: */
107: public int getDepth(Throwable ex) {
108: return getDepth(ex.getClass(), 0);
109: }
110:
111: private int getDepth(Class exceptionClass, int depth) {
112: if (exceptionClass.getName().indexOf(this .exceptionName) != -1) {
113: // Found it!
114: return depth;
115: }
116: // If we've gone as far as we can go and haven't found it...
117: if (exceptionClass.equals(Throwable.class)) {
118: return -1;
119: }
120: return getDepth(exceptionClass.getSuperclass(), depth + 1);
121: }
122:
123: public boolean equals(Object other) {
124: if (this == other) {
125: return true;
126: }
127: if (!(other instanceof RollbackRuleAttribute)) {
128: return false;
129: }
130: RollbackRuleAttribute rhs = (RollbackRuleAttribute) other;
131: return this .exceptionName.equals(rhs.exceptionName);
132: }
133:
134: public int hashCode() {
135: return this .exceptionName.hashCode();
136: }
137:
138: public String toString() {
139: return "RollbackRuleAttribute with pattern ["
140: + this .exceptionName + "]";
141: }
142:
143: }
|