jdk/src/share/classes/java/lang/Throwable.java
Print this page
*** 23,32 ****
--- 23,33 ----
* questions.
*/
package java.lang;
import java.io.*;
+ import java.util.*;
/**
* The <code>Throwable</code> class is the superclass of all errors and
* exceptions in the Java language. Only objects that are instances of this
* class (or one of its subclasses) are thrown by the Java Virtual Machine or
*** 100,110 ****
* <pre>
* try {
* lowLevelOp();
* } catch (LowLevelException le) {
* throw (HighLevelException)
! new HighLevelException().initCause(le); // Legacy constructor
* }
* </pre>
*
* <p>Prior to release 1.4, there were many throwables that had their own
* non-standard exception chaining mechanisms (
--- 101,111 ----
* <pre>
* try {
* lowLevelOp();
* } catch (LowLevelException le) {
* throw (HighLevelException)
! * new HighLevelException().initCause(le); // Legacy constructor
* }
* </pre>
*
* <p>Prior to release 1.4, there were many throwables that had their own
* non-standard exception chaining mechanisms (
*** 191,200 ****
--- 192,216 ----
* This field is lazily initialized on first use or serialization and
* nulled out when fillInStackTrace is called.
*/
/**
+ * The list of suppressed exceptions, as returned by
+ * {@link #getSuppressedExceptions()}.
+ *
+ * @serial
+ * @since 1.7
+ */
+ private List<Throwable> suppressedExceptions = Collections.emptyList();
+
+ /** Caption for labeling causative exception stack traces */
+ private static final String CAUSE_CAPTION = "Caused by: ";
+
+ /** Caption for labeling suppressed exception stack traces */
+ private static final String SUPPRESSED_CAPTION = "Suppressed: ";
+
+ /**
* Constructs a new throwable with <code>null</code> as its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* <p>The {@link #fillInStackTrace()} method is called to initialize
*** 467,476 ****
--- 483,538 ----
* }
*
* class LowLevelException extends Exception {
* }
* </pre>
+ * As of release 7, the platform supports the notion of
+ * <i>suppressed exceptions</i> (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:
+ *
+ * <pre>
+ * 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
+ * </pre>
+ * 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."
+ *
+ * <p>An exception can have both a cause and one or more suppressed
+ * exceptions:
+ * <pre>
+ * 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)
+ * </pre>
+ * Likewise, a suppressed exception can have a cause:
+ * <pre>
+ * 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.<init>(Resource2.java:45)
+ * ... 2 more
+ * </pre>
*/
public void printStackTrace() {
printStackTrace(System.err);
}
*** 478,573 ****
* Prints this throwable and its backtrace to the specified print stream.
*
* @param s <code>PrintStream</code> to use for output
*/
public void printStackTrace(PrintStream 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);
}
}
/**
! * Print our stack trace as a cause for the specified stack trace.
*/
! private void printStackTraceAsCause(PrintStream s,
! StackTraceElement[] causedTrace)
! {
! // assert Thread.holdsLock(s);
! // 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--;
}
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");
! // Recurse if we have a cause
Throwable ourCause = getCause();
if (ourCause != null)
! ourCause.printStackTraceAsCause(s, trace);
}
/**
* Prints this throwable and its backtrace to the specified
* print writer.
*
* @param s <code>PrintWriter</code> to use for output
* @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);
! }
}
/**
! * Print our stack trace as a cause for the specified stack trace.
*/
! private void printStackTraceAsCause(PrintWriter s,
! StackTraceElement[] causedTrace)
! {
! // assert Thread.holdsLock(s);
! // 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--;
}
- 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");
! // Recurse if we have a cause
! Throwable ourCause = getCause();
! if (ourCause != null)
! ourCause.printStackTraceAsCause(s, trace);
}
/**
* Fills in the execution stack trace. This method records within this
* <code>Throwable</code> object information about the current state of
--- 540,658 ----
* Prints this throwable and its backtrace to the specified print stream.
*
* @param s <code>PrintStream</code> to use for output
*/
public void printStackTrace(PrintStream s) {
! printStackTrace(new WrappedPrintStream(s));
! }
!
! private void printStackTrace(PrintStreamOrWriter s) {
! synchronized (s.lock()) {
! // Print our stack trace
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
! 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.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "");
}
}
/**
! * Print our stack trace as an enclosed exception for the specified
! * stack trace.
*/
! 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 enclosing trace
StackTraceElement[] trace = getOurStackTrace();
! 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;
! // 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(prefix + "\t... " + framesInCommon + " more");
! // 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.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix);
}
/**
* Prints this throwable and its backtrace to the specified
* print writer.
*
* @param s <code>PrintWriter</code> to use for output
* @since JDK1.1
*/
public void printStackTrace(PrintWriter s) {
! printStackTrace(new WrappedPrintWriter(s));
}
/**
! * Wrapper class for PrintStream and PrintWirter to enable a single
! * implementation of printStackTrace.
*/
! private abstract static class PrintStreamOrWriter {
! /** Returns the object to be locked when using this StreamOrWriter */
! abstract Object lock();
! /** 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;
! }
!
! Object lock() {
! return printStream;
! }
!
! 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);
! }
}
/**
* Fills in the execution stack trace. This method records within this
* <code>Throwable</code> object information about the current state of
*** 665,676 ****
--- 750,809 ----
* @throws IndexOutOfBoundsException if <tt>index < 0 ||
* index >= getStackTraceDepth() </tt>
*/
native StackTraceElement getStackTraceElement(int index);
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject(); // read in all fields
+ List<Throwable> impressedExceptions = Collections.emptyList();
+ if (suppressedExceptions != null &&
+ !suppressedExceptions.isEmpty()) { // Copy Throwables to new list
+ impressedExceptions = new ArrayList<Throwable>();
+ for(Throwable t : suppressedExceptions) {
+ impressedExceptions.add(t);
+ }
+ }
+ suppressedExceptions = impressedExceptions;
+ }
+
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
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<Throwable>();
+ suppressedExceptions.add(exception);
+ }
+
+ private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
+
+ /**
+ * Returns an array containing all of the exceptions that were
+ * suppressed, typically by the automatic resource management
+ * statement, in order to deliver this exception.
+ *
+ * @return an array containing all of the exceptions that were
+ * suppressed to deliver this exception.
+ * @since 1.7
+ */
+ public Throwable[] getSuppressedExceptions() {
+ return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
+ }
}