jdk/src/share/classes/java/lang/Throwable.java

Print this page

        

@@ -23,10 +23,11 @@
  * 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,11 +101,11 @@
  * <pre>
  *     try {
  *         lowLevelOp();
  *     } catch (LowLevelException le) {
  *         throw (HighLevelException)
-                 new HighLevelException().initCause(le);  // Legacy constructor
+ *               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,10 +192,25 @@
      * 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,10 +483,56 @@
      * }
      *
      * 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,96 +540,119 @@
      * 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) {
+        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");
+
+        // Print suppressed exceptions, if any
+        for (Throwable se : suppressedExceptions)
+            se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
+                    prefix +"\t");
 
-        // Recurse if we have a cause
+        // Print cause, if any
         Throwable ourCause = getCause();
         if (ourCause != null)
-            ourCause.printStackTraceAsCause(s, trace);
+            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) {
-        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);
         }
-        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");
+    private static class WrappedPrintStream extends PrintStreamOrWriter {
+        private final PrintStream printStream;
 
-        // Recurse if we have a cause
-        Throwable ourCause = getCause();
-        if (ourCause != null)
-            ourCause.printStackTraceAsCause(s, trace);
+        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

@@ -671,6 +756,40 @@
         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);
+    }
 }