Throwable
class is the superclass of all errors and
@@ -102,7 +103,7 @@
* lowLevelOp();
* } catch (LowLevelException le) {
* throw (HighLevelException)
- new HighLevelException().initCause(le); // Legacy constructor
+ * new HighLevelException().initCause(le); // Legacy constructor
* }
*
*
@@ -193,6 +194,21 @@
*/
/**
+ * The list of suppressed exceptions, as returned by
+ * {@link #getSuppressedExceptions()}.
+ *
+ * @serial
+ * @since 1.7
+ */
+ private Listnull
as its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
@@ -469,6 +485,52 @@
* class LowLevelException extends Exception {
* }
*
+ * As of release 7, the platform supports the notion of
+ * suppressed exceptions (in conjunction with automatic
+ * resource management blocks). Any exceptions that were
+ * suppressed in order to deliver an exception are printed out
+ * beneath the stack trace. The format of this information
+ * depends on the implementation, but the following example may be
+ * regarded as typical:
+ *
+ * + * Exception in thread "main" java.lang.Exception: Something happened + * at Foo.bar(Foo.java:10) + * at Foo.main(Foo.java:5) + * Suppressed: Resource$CloseFailException: Resource ID = 0 + * at Resource.close(Resource.java:26) + * at Foo.bar(Foo.java:9) + * ... 1 more + *+ * Note that the "... n more" notation is used on suppressed exceptions + * just at it is used on causes. Unlike causes, suppressed exceptions are + * indented beyond their "containing exceptions." + * + *
An exception can have both a cause and one or more suppressed + * exceptions: + *
+ * Exception in thread "main" java.lang.Exception: Main block + * at Foo3.main(Foo3.java:7) + * Suppressed: Resource$CloseFailException: Resource ID = 2 + * at Resource.close(Resource.java:26) + * at Foo3.main(Foo3.java:5) + * Suppressed: Resource$CloseFailException: Resource ID = 1 + * at Resource.close(Resource.java:26) + * at Foo3.main(Foo3.java:5) + * Caused by: java.lang.Exception: I did it + * at Foo3.main(Foo3.java:8) + *+ * Likewise, a suppressed exception can have a cause: + *
+ * Exception in thread "main" java.lang.Exception: Main block + * at Foo4.main(Foo4.java:6) + * Suppressed: Resource2$CloseFailException: Resource ID = 1 + * at Resource2.close(Resource2.java:20) + * at Foo4.main(Foo4.java:5) + * Caused by: java.lang.Exception: Rats, you caught me + * at Resource2$CloseFailException.*/ public void printStackTrace() { printStackTrace(System.err); @@ -480,44 +542,60 @@ * @param s(Resource2.java:45) + * ... 2 more + *
PrintStream
to use for output
*/
public void printStackTrace(PrintStream s) {
- synchronized (s) {
+ printStackTrace(new WrappedPrintStream(s));
+ }
+
+ private void printStackTrace(PrintStreamOrWriter s) {
+ synchronized (s.lock()) {
+ // Print our stack trace
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
- for (int i=0; i < trace.length; i++)
- s.println("\tat " + trace[i]);
+ for (StackTraceElement traceElement : trace)
+ s.println("\tat " + traceElement);
+ // Print suppressed exceptions, if any
+ for (Throwable se : suppressedExceptions)
+ se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t");
+
+ // Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
- ourCause.printStackTraceAsCause(s, trace);
+ ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "");
}
}
/**
- * Print our stack trace as a cause for the specified stack trace.
+ * Print our stack trace as an enclosed exception for the specified
+ * stack trace.
*/
- private void printStackTraceAsCause(PrintStream s,
- StackTraceElement[] causedTrace)
- {
- // assert Thread.holdsLock(s);
+ private void printEnclosedStackTrace(PrintStreamOrWriter s,
+ StackTraceElement[] enclosingTrace, String caption, String prefix) {
+ assert Thread.holdsLock(s.lock());
- // Compute number of frames in common between this and caused
+ // Compute number of frames in common between this and enclosing trace
StackTraceElement[] trace = getOurStackTrace();
- int m = trace.length-1, n = causedTrace.length-1;
- while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) {
+ int m = trace.length-1, n = enclosingTrace.length-1;
+ while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
m--; n--;
}
int framesInCommon = trace.length - 1 - m;
- s.println("Caused by: " + this);
- for (int i=0; i <= m; i++)
- s.println("\tat " + trace[i]);
+ // Print our stack trace
+ s.println(prefix + caption + this);
+ for (int i = 0; i <= m; i++)
+ s.println(prefix + "\tat " + trace[i]);
if (framesInCommon != 0)
- s.println("\t... " + framesInCommon + " more");
+ s.println(prefix + "\t... " + framesInCommon + " more");
- // Recurse if we have a cause
+ // Print suppressed exceptions, if any
+ for (Throwable se : suppressedExceptions)
+ se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
+ prefix +"\t");
+
+ // Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
- ourCause.printStackTraceAsCause(s, trace);
+ ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix);
}
/**
@@ -528,44 +606,51 @@
* @since JDK1.1
*/
public void printStackTrace(PrintWriter s) {
- synchronized (s) {
- s.println(this);
- StackTraceElement[] trace = getOurStackTrace();
- for (int i=0; i < trace.length; i++)
- s.println("\tat " + trace[i]);
-
- Throwable ourCause = getCause();
- if (ourCause != null)
- ourCause.printStackTraceAsCause(s, trace);
- }
+ printStackTrace(new WrappedPrintWriter(s));
}
/**
- * Print our stack trace as a cause for the specified stack trace.
+ * Wrapper class for PrintStream and PrintWirter to enable a single
+ * implementation of printStackTrace.
*/
- private void printStackTraceAsCause(PrintWriter s,
- StackTraceElement[] causedTrace)
- {
- // assert Thread.holdsLock(s);
+ private abstract static class PrintStreamOrWriter {
+ /** Returns the object to be locked when using this StreamOrWriter */
+ abstract Object lock();
- // Compute number of frames in common between this and caused
- StackTraceElement[] trace = getOurStackTrace();
- int m = trace.length-1, n = causedTrace.length-1;
- while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) {
- m--; n--;
+ /** Prints the specified string as a line on this StreamOrWriter */
+ abstract void println(Object o);
+ }
+
+ private static class WrappedPrintStream extends PrintStreamOrWriter {
+ private final PrintStream printStream;
+
+ WrappedPrintStream(PrintStream printStream) {
+ this.printStream = printStream;
}
- int framesInCommon = trace.length - 1 - m;
- s.println("Caused by: " + this);
- for (int i=0; i <= m; i++)
- s.println("\tat " + trace[i]);
- if (framesInCommon != 0)
- s.println("\t... " + framesInCommon + " more");
+ Object lock() {
+ return printStream;
+ }
- // Recurse if we have a cause
- Throwable ourCause = getCause();
- if (ourCause != null)
- ourCause.printStackTraceAsCause(s, trace);
+ void println(Object o) {
+ printStream.println(o);
+ }
+ }
+
+ private static class WrappedPrintWriter extends PrintStreamOrWriter {
+ private final PrintWriter printWriter;
+
+ WrappedPrintWriter(PrintWriter printWriter) {
+ this.printWriter = printWriter;
+ }
+
+ Object lock() {
+ return printWriter;
+ }
+
+ void println(Object o) {
+ printWriter.println(o);
+ }
}
/**
@@ -673,4 +758,38 @@
getOurStackTrace(); // Ensure that stackTrace field is initialized.
s.defaultWriteObject();
}
+
+ /**
+ * Adds the specified exception to the list of exceptions that
+ * were suppressed, typically by the automatic resource management
+ * statement, in order to deliver this exception.
+ *
+ * @param exception the exception to be added to the list of
+ * suppressed exceptions
+ * @throws NullPointerException if {@code exception} is null
+ * @since 1.7
+ */
+ public synchronized void addSuppressedException(Throwable exception) {
+ if (exception == null)
+ throw new NullPointerException();
+
+ if (suppressedExceptions.size() == 0)
+ suppressedExceptions = new ArrayList