author | darcy |
Wed, 23 Jun 2010 17:03:40 -0700 | |
changeset 5972 | e3f47656e9d9 |
parent 5971 | 7f83fb438899 |
child 5973 | b48e1702532e |
--- a/jdk/make/java/java/FILES_java.gmk Wed Jun 23 21:22:27 2010 +0100 +++ b/jdk/make/java/java/FILES_java.gmk Wed Jun 23 17:03:40 2010 -0700 @@ -30,6 +30,7 @@ # JAVA_JAVA_java = \ java/lang/Object.java \ + java/lang/AutoCloseable.java \ java/lang/Class.java \ java/lang/Thread.java \ java/lang/Character.java \
--- a/jdk/src/share/classes/java/io/Closeable.java Wed Jun 23 21:22:27 2010 +0100 +++ b/jdk/src/share/classes/java/io/Closeable.java Wed Jun 23 17:03:40 2010 -0700 @@ -28,14 +28,14 @@ import java.io.IOException; /** - * A <tt>Closeable</tt> is a source or destination of data that can be closed. + * A {@code Closeable} is a source or destination of data that can be closed. * The close method is invoked to release resources that the object is * holding (such as open files). * * @since 1.5 */ -public interface Closeable { +public interface Closeable extends AutoCloseable { /** * Closes this stream and releases any system resources associated @@ -45,5 +45,4 @@ * @throws IOException if an I/O error occurs */ public void close() throws IOException; - }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/lang/AutoCloseable.java Wed Jun 23 17:03:40 2010 -0700 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * A resource that must be closed when it is no longer needed. + * + * @author Josh Bloch + * @since 1.7 + */ +public interface AutoCloseable { + /** + * Close this resource, relinquishing any underlying resources. + * This method is invoked automatically by the automatic resource + * management block construct. + * + * <p>Classes implementing this method are strongly encouraged to + * be declared to throw more specific exceptions (or no exception + * at all, if the close cannot fail). + * + * @throws Exception if this resource cannot be closed + */ + void close() throws Exception; +}
--- a/jdk/src/share/classes/java/lang/Throwable.java Wed Jun 23 21:22:27 2010 +0100 +++ b/jdk/src/share/classes/java/lang/Throwable.java Wed Jun 23 17:03:40 2010 -0700 @@ -25,6 +25,7 @@ package java.lang; import java.io.*; +import java.util.*; /** * The <code>Throwable</code> 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 * } * </pre> * @@ -193,6 +194,24 @@ */ /** + * The list of suppressed exceptions, as returned by + * {@link #getSuppressedExceptions()}. + * + * @serial + * @since 1.7 + */ + private List<Throwable> suppressedExceptions = Collections.emptyList(); + + /** Message for trying to suppress a null exception. */ + private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; + + /** 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}. @@ -469,6 +488,52 @@ * 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); @@ -480,44 +545,71 @@ * @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) { + Set<Throwable> dejaVu = new HashSet<Throwable>(); + dejaVu.add(this); + + 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", dejaVu); + + // Print cause, if any Throwable ourCause = getCause(); if (ourCause != null) - ourCause.printStackTraceAsCause(s, trace); + ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu); } } /** - * 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, + Set<Throwable> dejaVu) { + assert Thread.holdsLock(s.lock()); + if (dejaVu.contains(this)) { + s.println("\t[CIRCULAR REFERENCE:" + this + "]"); + } else { + dejaVu.add(this); + // Compute number of frames in common between this and enclosing trace + StackTraceElement[] trace = getOurStackTrace(); + int m = trace.length - 1; + int n = enclosingTrace.length - 1; + while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) { + m--; n--; + } + int framesInCommon = trace.length - 1 - m; - // 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--; + // 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", dejaVu); + + // Print cause, if any + Throwable ourCause = getCause(); + if (ourCause != null) + ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu); } - 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); } /** @@ -528,44 +620,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 PrintWriter 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(); + + /** Prints the specified string as a line on this StreamOrWriter */ + abstract void println(Object o); + } - // 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--; + private static class WrappedPrintStream extends PrintStreamOrWriter { + private final PrintStream printStream; + + WrappedPrintStream(PrintStream printStream) { + this.printStream = printStream; + } + + Object lock() { + return printStream; } - int framesInCommon = trace.length - 1 - m; + + void println(Object o) { + printStream.println(o); + } + } + + private static class WrappedPrintWriter extends PrintStreamOrWriter { + private final PrintWriter printWriter; - 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"); + WrappedPrintWriter(PrintWriter printWriter) { + this.printWriter = printWriter; + } - // Recurse if we have a cause - Throwable ourCause = getCause(); - if (ourCause != null) - ourCause.printStackTraceAsCause(s, trace); + Object lock() { + return printWriter; + } + + void println(Object o) { + printWriter.println(o); + } } /** @@ -667,10 +766,60 @@ */ native StackTraceElement getStackTraceElement(int index); - private synchronized void writeObject(java.io.ObjectOutputStream s) + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject(); // read in all fields + List<Throwable> suppressed = Collections.emptyList(); + if (suppressedExceptions != null && + !suppressedExceptions.isEmpty()) { // Copy Throwables to new list + suppressed = new ArrayList<Throwable>(); + for(Throwable t : suppressedExceptions) { + if (t == null) + throw new NullPointerException(NULL_CAUSE_MESSAGE); + suppressed.add(t); + } + } + suppressedExceptions = suppressed; + } + + private synchronized void writeObject(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(NULL_CAUSE_MESSAGE); + + 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); + } }
--- a/jdk/src/share/classes/java/nio/channels/FileLock.java Wed Jun 23 21:22:27 2010 +0100 +++ b/jdk/src/share/classes/java/nio/channels/FileLock.java Wed Jun 23 17:03:40 2010 -0700 @@ -116,7 +116,7 @@ * @since 1.4 */ -public abstract class FileLock { +public abstract class FileLock implements AutoCloseable { private final Channel channel; private final long position; @@ -299,6 +299,17 @@ public abstract void release() throws IOException; /** + * This method invokes the {@link #release} method. It was added + * to the class so that it could be used in conjunction with the + * automatic resource management block construct. + * + * @since 1.7 + */ + public final void close() throws IOException { + release(); + } + + /** * Returns a string describing the range, type, and validity of this lock. * * @return A descriptive string
--- a/jdk/src/share/classes/javax/imageio/stream/ImageInputStream.java Wed Jun 23 21:22:27 2010 +0100 +++ b/jdk/src/share/classes/javax/imageio/stream/ImageInputStream.java Wed Jun 23 17:03:40 2010 -0700 @@ -25,6 +25,7 @@ package javax.imageio.stream; +import java.io.Closeable; import java.io.DataInput; import java.io.IOException; import java.nio.ByteOrder; @@ -42,7 +43,7 @@ * @see MemoryCacheImageInputStream * */ -public interface ImageInputStream extends DataInput { +public interface ImageInputStream extends DataInput, Closeable { /** * Sets the desired byte order for future reads of data values
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/Throwable/SuppressedExceptions.java Wed Jun 23 17:03:40 2010 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; +import java.util.*; + +/* + * @test + * @bug 6911258 6962571 + * @summary Basic tests of suppressed exceptions + * @author Joseph D. Darcy + */ + +public class SuppressedExceptions { + private static String message = "Bad suppressed exception information"; + + public static void main(String... args) throws Exception { + basicSupressionTest(); + serializationTest(); + selfReference(); + } + + private static void basicSupressionTest() { + Throwable throwable = new Throwable(); + RuntimeException suppressed = new RuntimeException("A suppressed exception."); + AssertionError repressed = new AssertionError("A repressed error."); + + Throwable[] t0 = throwable.getSuppressedExceptions(); + if (t0.length != 0) { + throw new RuntimeException(message); + } + throwable.printStackTrace(); + + throwable.addSuppressedException(suppressed); + Throwable[] t1 = throwable.getSuppressedExceptions(); + if (t1.length != 1 || + t1[0] != suppressed) {throw new RuntimeException(message); + } + throwable.printStackTrace(); + + throwable.addSuppressedException(repressed); + Throwable[] t2 = throwable.getSuppressedExceptions(); + if (t2.length != 2 || + t2[0] != suppressed || + t2[1] != repressed) { + throw new RuntimeException(message); + } + throwable.printStackTrace(); + } + + private static void serializationTest() throws Exception { + /* + * Bytes of the serial form of + * + * (new Throwable())setStackTrace(new StackTraceElement[0]) + * + * from JDK 6; suppressedException field will be missing and + * thus default to null upon deserialization. + */ + byte[] bytes = { + (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13, + (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, + (byte)0x67, (byte)0x2e, (byte)0x54, (byte)0x68, (byte)0x72, (byte)0x6f, (byte)0x77, (byte)0x61, + (byte)0x62, (byte)0x6c, (byte)0x65, (byte)0xd5, (byte)0xc6, (byte)0x35, (byte)0x27, (byte)0x39, + (byte)0x77, (byte)0xb8, (byte)0xcb, (byte)0x03, (byte)0x00, (byte)0x03, (byte)0x4c, (byte)0x00, + (byte)0x05, (byte)0x63, (byte)0x61, (byte)0x75, (byte)0x73, (byte)0x65, (byte)0x74, (byte)0x00, + (byte)0x15, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c, + (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f, (byte)0x54, (byte)0x68, (byte)0x72, (byte)0x6f, + (byte)0x77, (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65, (byte)0x3b, (byte)0x4c, (byte)0x00, + (byte)0x0d, (byte)0x64, (byte)0x65, (byte)0x74, (byte)0x61, (byte)0x69, (byte)0x6c, (byte)0x4d, + (byte)0x65, (byte)0x73, (byte)0x73, (byte)0x61, (byte)0x67, (byte)0x65, (byte)0x74, (byte)0x00, + (byte)0x12, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c, + (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f, (byte)0x53, (byte)0x74, (byte)0x72, (byte)0x69, + (byte)0x6e, (byte)0x67, (byte)0x3b, (byte)0x5b, (byte)0x00, (byte)0x0a, (byte)0x73, (byte)0x74, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x54, (byte)0x72, (byte)0x61, (byte)0x63, (byte)0x65, + (byte)0x74, (byte)0x00, (byte)0x1e, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, + (byte)0x61, (byte)0x2f, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f, (byte)0x53, + (byte)0x74, (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x54, (byte)0x72, (byte)0x61, (byte)0x63, + (byte)0x65, (byte)0x45, (byte)0x6c, (byte)0x65, (byte)0x6d, (byte)0x65, (byte)0x6e, (byte)0x74, + (byte)0x3b, (byte)0x78, (byte)0x70, (byte)0x71, (byte)0x00, (byte)0x7e, (byte)0x00, (byte)0x04, + (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00, (byte)0x1e, (byte)0x5b, (byte)0x4c, (byte)0x6a, + (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, + (byte)0x2e, (byte)0x53, (byte)0x74, (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x54, (byte)0x72, + (byte)0x61, (byte)0x63, (byte)0x65, (byte)0x45, (byte)0x6c, (byte)0x65, (byte)0x6d, (byte)0x65, + (byte)0x6e, (byte)0x74, (byte)0x3b, (byte)0x02, (byte)0x46, (byte)0x2a, (byte)0x3c, (byte)0x3c, + (byte)0xfd, (byte)0x22, (byte)0x39, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0xac, (byte)0xed, (byte)0x00, + (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13, (byte)0x6a, (byte)0x61, (byte)0x76, + (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x54, + (byte)0x68, (byte)0x72, (byte)0x6f, (byte)0x77, (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65, + (byte)0xd5, (byte)0xc6, (byte)0x35, (byte)0x27, (byte)0x39, (byte)0x77, (byte)0xb8, (byte)0xcb, + (byte)0x03, (byte)0x00, (byte)0x03, (byte)0x4c, (byte)0x00, (byte)0x05, (byte)0x63, (byte)0x61, + (byte)0x75, (byte)0x73, (byte)0x65, (byte)0x74, (byte)0x00, (byte)0x15, (byte)0x4c, (byte)0x6a, + (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, + (byte)0x2f, (byte)0x54, (byte)0x68, (byte)0x72, (byte)0x6f, (byte)0x77, (byte)0x61, (byte)0x62, + (byte)0x6c, (byte)0x65, (byte)0x3b, (byte)0x4c, (byte)0x00, (byte)0x0d, (byte)0x64, (byte)0x65, + (byte)0x74, (byte)0x61, (byte)0x69, (byte)0x6c, (byte)0x4d, (byte)0x65, (byte)0x73, (byte)0x73, + (byte)0x61, (byte)0x67, (byte)0x65, (byte)0x74, (byte)0x00, (byte)0x12, (byte)0x4c, (byte)0x6a, + (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c, (byte)0x6e, (byte)0x67, (byte)0x3b, + (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f, (byte)0x53, (byte)0x74, (byte)0x72, (byte)0x69, + (byte)0x5b, (byte)0x00, (byte)0x0a, (byte)0x73, (byte)0x74, (byte)0x61, (byte)0x63, (byte)0x6b, + (byte)0x54, (byte)0x72, (byte)0x61, (byte)0x63, (byte)0x65, (byte)0x74, (byte)0x00, (byte)0x1e, + (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c, + (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f, (byte)0x53, (byte)0x74, (byte)0x61, (byte)0x63, + (byte)0x6b, (byte)0x54, (byte)0x72, (byte)0x61, (byte)0x63, (byte)0x65, (byte)0x45, (byte)0x6c, + (byte)0x65, (byte)0x6d, (byte)0x65, (byte)0x6e, (byte)0x74, (byte)0x3b, (byte)0x78, (byte)0x70, + (byte)0x71, (byte)0x00, (byte)0x7e, (byte)0x00, (byte)0x04, (byte)0x70, (byte)0x75, (byte)0x72, + (byte)0x00, (byte)0x1e, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, + (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x53, (byte)0x74, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x54, (byte)0x72, (byte)0x61, (byte)0x63, (byte)0x65, + (byte)0x45, (byte)0x6c, (byte)0x65, (byte)0x6d, (byte)0x65, (byte)0x6e, (byte)0x74, (byte)0x3b, + (byte)0x02, (byte)0x46, (byte)0x2a, (byte)0x3c, (byte)0x3c, (byte)0xfd, (byte)0x22, (byte)0x39, + (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, + }; + + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais); + + Object o = ois.readObject(); + Throwable throwable = (Throwable) o; + + System.err.println("TESTING SERIALIZED EXCEPTION"); + + Throwable[] t0 = throwable.getSuppressedExceptions(); + if (t0.length != 0) { // Will fail if t0 is null. + throw new RuntimeException(message); + } + throwable.printStackTrace(); + } + + private static void selfReference() { + Throwable throwable1 = new RuntimeException(); + Throwable throwable2 = new AssertionError(); + throwable1.initCause(throwable2); + throwable2.initCause(throwable1); + + throwable1.printStackTrace(); + + + throwable1.addSuppressedException(throwable1); + throwable1.addSuppressedException(throwable2); + + throwable1.printStackTrace(); + } +}