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.core;
018:
019: import java.io.PrintStream;
020: import java.io.PrintWriter;
021:
022: /**
023: * Handy class for wrapping checked <code>Exceptions</code> with a root cause.
024: *
025: * <p>This time-honored technique is no longer necessary in Java 1.4, which
026: * finally provides built-in support for exception nesting. Thus exceptions in
027: * applications written to use Java 1.4 need not extend this class. To ease
028: * migration, this class mirrors Java 1.4's nested exceptions as closely as possible.
029: *
030: * <p>This class is <code>abstract</code> to force the programmer to extend
031: * the class. <code>getMessage</code> will include nested exception
032: * information; <code>printStackTrace</code> and other like methods will
033: * delegate to the wrapped exception, if any.
034: *
035: * <p>The similarity between this class and the {@link NestedRuntimeException}
036: * class is unavoidable, as Java forces these two classes to have different
037: * superclasses (ah, the inflexibility of concrete inheritance!).
038: *
039: * @author Rod Johnson
040: * @author Juergen Hoeller
041: * @see #getMessage
042: * @see #printStackTrace
043: * @see NestedRuntimeException
044: */
045: public abstract class NestedCheckedException extends Exception {
046:
047: /** Use serialVersionUID from Spring 1.2 for interoperability */
048: private static final long serialVersionUID = 7100714597678207546L;
049:
050: /** Root cause of this nested exception */
051: private Throwable cause;
052:
053: /**
054: * Construct a <code>NestedCheckedException</code> with the specified detail message.
055: * @param msg the detail message
056: */
057: public NestedCheckedException(String msg) {
058: super (msg);
059: }
060:
061: /**
062: * Construct a <code>NestedCheckedException</code> with the specified detail message
063: * and nested exception.
064: * @param msg the detail message
065: * @param cause the nested exception
066: */
067: public NestedCheckedException(String msg, Throwable cause) {
068: super (msg);
069: this .cause = cause;
070: }
071:
072: /**
073: * Return the nested cause, or <code>null</code> if none.
074: * <p>Note that this will only check one level of nesting.
075: * Use {@link #getRootCause()} to retrieve the innermost cause.
076: */
077: public Throwable getCause() {
078: // Even if you cannot set the cause of this exception other than through
079: // the constructor, we check for the cause being "this" here, as the cause
080: // could still be set to "this" via reflection: for example, by a remoting
081: // deserializer like Hessian's.
082: return (this .cause == this ? null : this .cause);
083: }
084:
085: /**
086: * Return the detail message, including the message from the nested exception
087: * if there is one.
088: */
089: public String getMessage() {
090: return NestedExceptionUtils.buildMessage(super .getMessage(),
091: getCause());
092: }
093:
094: /**
095: * Print the composite message and the embedded stack trace to the specified stream.
096: * @param ps the print stream
097: */
098: public void printStackTrace(PrintStream ps) {
099: if (getCause() == null) {
100: super .printStackTrace(ps);
101: } else {
102: ps.println(this );
103: ps.print("Caused by: ");
104: getCause().printStackTrace(ps);
105: }
106: }
107:
108: /**
109: * Print the composite message and the embedded stack trace to the specified print writer.
110: * @param pw the print writer
111: */
112: public void printStackTrace(PrintWriter pw) {
113: if (getCause() == null) {
114: super .printStackTrace(pw);
115: } else {
116: pw.println(this );
117: pw.print("Caused by: ");
118: getCause().printStackTrace(pw);
119: }
120: }
121:
122: /**
123: * Retrieve the innermost cause of this exception, if any.
124: * <p>Currently just traverses NestedCheckedException causes. Will use
125: * the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
126: * @return the innermost exception, or <code>null</code> if none
127: */
128: public Throwable getRootCause() {
129: Throwable cause = getCause();
130: if (cause instanceof NestedCheckedException) {
131: return ((NestedCheckedException) cause).getRootCause();
132: } else {
133: return cause;
134: }
135: }
136:
137: /**
138: * Retrieve the most specific cause of this exception, that is,
139: * either the innermost cause (root cause) or this exception itself.
140: * <p>Differs from {@link #getRootCause()} in that it falls back
141: * to the present exception if there is no root cause.
142: * @return the most specific cause (never <code>null</code>)
143: * @since 2.0.3
144: */
145: public Throwable getMostSpecificCause() {
146: Throwable rootCause = getRootCause();
147: return (rootCause != null ? rootCause : this );
148: }
149:
150: /**
151: * Check whether this exception contains an exception of the given type:
152: * either it is of the given class itself or it contains a nested cause
153: * of the given type.
154: * <p>Currently just traverses <code>NestedCheckedException</code> causes.
155: * Will use the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
156: * @param exType the exception type to look for
157: * @return whether there is a nested exception of the specified type
158: */
159: public boolean contains(Class exType) {
160: if (exType == null) {
161: return false;
162: }
163: if (exType.isInstance(this )) {
164: return true;
165: }
166: Throwable cause = getCause();
167: if (cause instanceof NestedCheckedException) {
168: return ((NestedCheckedException) cause).contains(exType);
169: } else {
170: return (cause != null && exType.isInstance(cause));
171: }
172: }
173:
174: }
|