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 runtime <code>Exceptions</code> with a root cause.
024: *
025: * <p>This time-honoured 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 NestedCheckedException}
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 NestedCheckedException
044: */
045: public abstract class NestedRuntimeException extends RuntimeException {
046:
047: /** Use serialVersionUID from Spring 1.2 for interoperability */
048: private static final long serialVersionUID = 5439915454935047936L;
049:
050: /** Root cause of this nested exception */
051: private Throwable cause;
052:
053: /**
054: * Construct a <code>NestedRuntimeException</code> with the specified detail message.
055: * @param msg the detail message
056: */
057: public NestedRuntimeException(String msg) {
058: super (msg);
059: }
060:
061: /**
062: * Construct a <code>NestedRuntimeException</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 NestedRuntimeException(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 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 <code>NestedRuntimeException</code> causes.
125: * Will use 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: * @since 2.0
128: */
129: public Throwable getRootCause() {
130: Throwable cause = getCause();
131: if (cause instanceof NestedRuntimeException) {
132: Throwable rootCause = ((NestedRuntimeException) cause)
133: .getRootCause();
134: return (rootCause != null ? rootCause : cause);
135: } else {
136: return cause;
137: }
138: }
139:
140: /**
141: * Retrieve the most specific cause of this exception, that is,
142: * either the innermost cause (root cause) or this exception itself.
143: * <p>Differs from {@link #getRootCause()} in that it falls back
144: * to the present exception if there is no root cause.
145: * @return the most specific cause (never <code>null</code>)
146: * @since 2.0.3
147: */
148: public Throwable getMostSpecificCause() {
149: Throwable rootCause = getRootCause();
150: return (rootCause != null ? rootCause : this );
151: }
152:
153: /**
154: * Check whether this exception contains an exception of the given type:
155: * either it is of the given class itself or it contains a nested cause
156: * of the given type.
157: * <p>Currently just traverses <code>NestedRuntimeException</code> causes.
158: * Will use the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
159: * @param exType the exception type to look for
160: * @return whether there is a nested exception of the specified type
161: */
162: public boolean contains(Class exType) {
163: if (exType == null) {
164: return false;
165: }
166: if (exType.isInstance(this )) {
167: return true;
168: }
169: Throwable cause = getCause();
170: if (cause instanceof NestedRuntimeException) {
171: return ((NestedRuntimeException) cause).contains(exType);
172: } else {
173: return (cause != null && exType.isInstance(cause));
174: }
175: }
176:
177: }
|