001: /*
002: * Copyright 2004-2006 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.compass.core.util;
018:
019: import java.io.PrintStream;
020: import java.io.PrintWriter;
021:
022: /**
023: * Handy class for wrapping runtime Exceptions 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>Abstract to force the programmer to extend the class. <code>getMessage</code>
031: * will include nested exception information; <code>printStackTrace</code> etc will
032: * delegate to the wrapped exception, if any.
033: *
034: * <p>The similarity between this class and the NestedCheckedException class is
035: * unavoidable, as Java forces these two classes to have different superclasses
036: * (ah, the inflexibility of concrete inheritance!).
037: *
038: * @author kimchy
039: * @see #getMessage
040: * @see #printStackTrace
041: * @see NestedCheckedException
042: */
043: public abstract class NestedRuntimeException extends RuntimeException {
044:
045: /** Root cause of this nested exception */
046: private Throwable cause;
047:
048: /**
049: * Construct a <code>NestedRuntimeException</code> with the specified detail message.
050: * @param msg the detail message
051: */
052: public NestedRuntimeException(String msg) {
053: super (msg);
054: }
055:
056: /**
057: * Construct a <code>NestedRuntimeException</code> with the specified detail message
058: * and nested exception.
059: * @param msg the detail message
060: * @param ex the nested exception
061: */
062: public NestedRuntimeException(String msg, Throwable ex) {
063: super (msg);
064: this .cause = ex;
065: }
066:
067: /**
068: * Return the nested cause, or <code>null</code> if none.
069: */
070: public Throwable getCause() {
071: // Even if you cannot set the cause of this exception other than through
072: // the constructor, we check for the cause being "this" here, as the cause
073: // could still be set to "this" via reflection: for example, by a remoting
074: // deserializer like Hessian's.
075: return (this .cause == this ? null : this .cause);
076: }
077:
078: /**
079: * Return the detail message, including the message from the nested exception
080: * if there is one.
081: */
082: public String getMessage() {
083: if (getCause() == null) {
084: return super .getMessage();
085: } else {
086: return super .getMessage() + "; nested exception is "
087: + getCause().getClass().getName() + ": "
088: + getCause().getMessage();
089: }
090: }
091:
092: /**
093: * Print the composite message and the embedded stack trace to the specified stream.
094: * @param ps the print stream
095: */
096: public void printStackTrace(PrintStream ps) {
097: if (getCause() == null) {
098: super .printStackTrace(ps);
099: } else {
100: ps.println(this );
101: getCause().printStackTrace(ps);
102: }
103: }
104:
105: /**
106: * Print the composite message and the embedded stack trace to the specified writer.
107: * @param pw the print writer
108: */
109: public void printStackTrace(PrintWriter pw) {
110: if (getCause() == null) {
111: super .printStackTrace(pw);
112: } else {
113: pw.println(this );
114: getCause().printStackTrace(pw);
115: }
116: }
117:
118: /**
119: * Check whether this exception contains an exception of the given class:
120: * either it is of the given class itself or it contains a nested cause
121: * of the given class.
122: * <p>Currently just traverses NestedRuntimeException causes. Will use
123: * the JDK 1.4 exception cause mechanism once Spring requires JDK 1.4.
124: * @param exClass the exception class to look for
125: */
126: public boolean contains(Class exClass) {
127: if (exClass == null) {
128: return false;
129: }
130: Throwable ex = this ;
131: while (ex != null) {
132: if (exClass.isInstance(ex)) {
133: return true;
134: }
135: if (ex instanceof NestedRuntimeException) {
136: // Cast is necessary on JDK 1.3, where Throwable does not
137: // provide a "getCause" method itself.
138: ex = ((NestedRuntimeException) ex).getCause();
139: } else {
140: ex = null;
141: }
142: }
143: return false;
144: }
145:
146: }
|