168 * @serial |
168 * @serial |
169 */ |
169 */ |
170 private String detailMessage; |
170 private String detailMessage; |
171 |
171 |
172 /** |
172 /** |
|
173 * A shared value for an empty stack. |
|
174 */ |
|
175 private static final StackTraceElement[] EMPTY_STACK = new StackTraceElement[0]; |
|
176 |
|
177 /* |
|
178 * To allow Throwable objects to be made immutable and safely |
|
179 * reused by the JVM, such as OutOfMemoryErrors, fields of |
|
180 * Throwable that are writable in response to user actions, cause |
|
181 * and suppressedExceptions obey the following protocol: |
|
182 * |
|
183 * 1) The fields are initialized to a non-null sentinel value |
|
184 * which indicates the value has logically not been set. |
|
185 * |
|
186 * 2) Writing a null to the field indicates further writes |
|
187 * are forbidden |
|
188 * |
|
189 * 3) The sentinel value may be replaced with another non-null |
|
190 * value. |
|
191 * |
|
192 * For example, implementations of the HotSpot JVM have |
|
193 * preallocated OutOfMemoryError objects to provide for better |
|
194 * diagnosability of that situation. These objects are created |
|
195 * without calling the constructor for that class and the fields |
|
196 * in question are initialized to null. To support this |
|
197 * capability, any new fields added to Throwable that require |
|
198 * being initialized to a non-null value require a coordinated JVM |
|
199 * change. |
|
200 */ |
|
201 |
|
202 /** |
173 * The throwable that caused this throwable to get thrown, or null if this |
203 * The throwable that caused this throwable to get thrown, or null if this |
174 * throwable was not caused by another throwable, or if the causative |
204 * throwable was not caused by another throwable, or if the causative |
175 * throwable is unknown. If this field is equal to this throwable itself, |
205 * throwable is unknown. If this field is equal to this throwable itself, |
176 * it indicates that the cause of this throwable has not yet been |
206 * it indicates that the cause of this throwable has not yet been |
177 * initialized. |
207 * initialized. |
186 * |
216 * |
187 * @serial |
217 * @serial |
188 * @since 1.4 |
218 * @since 1.4 |
189 */ |
219 */ |
190 private StackTraceElement[] stackTrace; |
220 private StackTraceElement[] stackTrace; |
191 /* |
221 |
192 * This field is lazily initialized on first use or serialization and |
222 // Setting this static field introduces an acceptable |
193 * nulled out when fillInStackTrace is called. |
223 // initialization dependency on a few java.util classes. |
194 */ |
224 private static final List<Throwable> SUPPRESSED_SENTINEL = |
195 |
225 Collections.unmodifiableList(new ArrayList<Throwable>(0)); |
196 /** |
226 |
197 * The list of suppressed exceptions, as returned by |
227 /** |
198 * {@link #getSuppressedExceptions()}. |
228 * The list of suppressed exceptions, as returned by {@link |
|
229 * #getSuppressed()}. The list is initialized to a zero-element |
|
230 * unmodifiable sentinel list. When a serialized Throwable is |
|
231 * read in, if the {@code suppressedExceptions} field points to a |
|
232 * zero-element list, the field is reset to the sentinel value. |
199 * |
233 * |
200 * @serial |
234 * @serial |
201 * @since 1.7 |
235 * @since 1.7 |
202 */ |
236 */ |
203 private List<Throwable> suppressedExceptions = null; |
237 private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL; |
204 /* |
|
205 * This field is lazily initialized when the first suppressed |
|
206 * exception is added. |
|
207 * |
|
208 * OutOfMemoryError is preallocated in the VM for better OOM |
|
209 * diagnosability during VM initialization. Constructor can't |
|
210 * be not invoked. If a new field to be added in the future must |
|
211 * be initialized to non-null, it requires a synchronized VM change. |
|
212 */ |
|
213 |
238 |
214 /** Message for trying to suppress a null exception. */ |
239 /** Message for trying to suppress a null exception. */ |
215 private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; |
240 private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; |
|
241 |
|
242 /** Message for trying to suppress oneself. */ |
|
243 private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted"; |
216 |
244 |
217 /** Caption for labeling causative exception stack traces */ |
245 /** Caption for labeling causative exception stack traces */ |
218 private static final String CAUSE_CAPTION = "Caused by: "; |
246 private static final String CAUSE_CAPTION = "Caused by: "; |
219 |
247 |
220 /** Caption for labeling suppressed exception stack traces */ |
248 /** Caption for labeling suppressed exception stack traces */ |
570 StackTraceElement[] trace = getOurStackTrace(); |
598 StackTraceElement[] trace = getOurStackTrace(); |
571 for (StackTraceElement traceElement : trace) |
599 for (StackTraceElement traceElement : trace) |
572 s.println("\tat " + traceElement); |
600 s.println("\tat " + traceElement); |
573 |
601 |
574 // Print suppressed exceptions, if any |
602 // Print suppressed exceptions, if any |
575 for (Throwable se : getSuppressedExceptions()) |
603 for (Throwable se : getSuppressed()) |
576 se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); |
604 se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); |
577 |
605 |
578 // Print cause, if any |
606 // Print cause, if any |
579 Throwable ourCause = getCause(); |
607 Throwable ourCause = getCause(); |
580 if (ourCause != null) |
608 if (ourCause != null) |
611 s.println(prefix + "\tat " + trace[i]); |
639 s.println(prefix + "\tat " + trace[i]); |
612 if (framesInCommon != 0) |
640 if (framesInCommon != 0) |
613 s.println(prefix + "\t... " + framesInCommon + " more"); |
641 s.println(prefix + "\t... " + framesInCommon + " more"); |
614 |
642 |
615 // Print suppressed exceptions, if any |
643 // Print suppressed exceptions, if any |
616 for (Throwable se : getSuppressedExceptions()) |
644 for (Throwable se : getSuppressed()) |
617 se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, |
645 se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, |
618 prefix +"\t", dejaVu); |
646 prefix +"\t", dejaVu); |
619 |
647 |
620 // Print cause, if any |
648 // Print cause, if any |
621 Throwable ourCause = getCause(); |
649 Throwable ourCause = getCause(); |
778 * @throws IndexOutOfBoundsException if {@code index < 0 || |
806 * @throws IndexOutOfBoundsException if {@code index < 0 || |
779 * index >= getStackTraceDepth() } |
807 * index >= getStackTraceDepth() } |
780 */ |
808 */ |
781 native StackTraceElement getStackTraceElement(int index); |
809 native StackTraceElement getStackTraceElement(int index); |
782 |
810 |
|
811 /** |
|
812 * Read a {@code Throwable} from a stream, enforcing |
|
813 * well-formedness constraints on fields. Null entries and |
|
814 * self-pointers are not allowed in the list of {@code |
|
815 * suppressedExceptions}. Null entries are not allowed for stack |
|
816 * trace elements. |
|
817 * |
|
818 * Note that there are no constraints on the value the {@code |
|
819 * cause} field can hold; both {@code null} and {@code this} are |
|
820 * valid values for the field. |
|
821 */ |
783 private void readObject(ObjectInputStream s) |
822 private void readObject(ObjectInputStream s) |
784 throws IOException, ClassNotFoundException { |
823 throws IOException, ClassNotFoundException { |
785 s.defaultReadObject(); // read in all fields |
824 s.defaultReadObject(); // read in all fields |
786 List<Throwable> suppressed = null; |
825 if (suppressedExceptions != null) { |
787 if (suppressedExceptions != null && |
826 List<Throwable> suppressed = null; |
788 !suppressedExceptions.isEmpty()) { // Copy Throwables to new list |
827 if (suppressedExceptions.isEmpty()) { |
789 suppressed = new ArrayList<Throwable>(); |
828 // Use the sentinel for a zero-length list |
790 for (Throwable t : suppressedExceptions) { |
829 suppressed = SUPPRESSED_SENTINEL; |
791 if (t == null) |
830 } else { // Copy Throwables to new list |
792 throw new NullPointerException(NULL_CAUSE_MESSAGE); |
831 suppressed = new ArrayList<Throwable>(1); |
793 suppressed.add(t); |
832 for (Throwable t : suppressedExceptions) { |
|
833 // Enforce constraints on suppressed exceptions in |
|
834 // case of corrupt or malicious stream. |
|
835 if (t == null) |
|
836 throw new NullPointerException(NULL_CAUSE_MESSAGE); |
|
837 if (t == this) |
|
838 throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); |
|
839 suppressed.add(t); |
|
840 } |
794 } |
841 } |
795 } |
842 suppressedExceptions = suppressed; |
796 suppressedExceptions = suppressed; |
843 } // else a null suppressedExceptions field remains null |
797 } |
844 |
798 |
845 if (stackTrace != null) { |
|
846 for (StackTraceElement ste : stackTrace) { |
|
847 if (ste == null) |
|
848 throw new NullPointerException("null StackTraceElement in serial stream. "); |
|
849 } |
|
850 } else { |
|
851 // A null stackTrace field in the serial form can result from |
|
852 // an exception serialized without that field in older JDK releases. |
|
853 stackTrace = EMPTY_STACK; |
|
854 } |
|
855 |
|
856 } |
|
857 |
|
858 /** |
|
859 * Write a {@code Throwable} object to a stream. |
|
860 */ |
799 private synchronized void writeObject(ObjectOutputStream s) |
861 private synchronized void writeObject(ObjectOutputStream s) |
800 throws IOException |
862 throws IOException { |
801 { |
|
802 getOurStackTrace(); // Ensure that stackTrace field is initialized. |
863 getOurStackTrace(); // Ensure that stackTrace field is initialized. |
803 s.defaultWriteObject(); |
864 s.defaultWriteObject(); |
804 } |
865 } |
805 |
866 |
806 /** |
867 /** |
807 * Adds the specified exception to the list of exceptions that |
868 * Adds the specified exception to the list of exceptions that |
808 * were suppressed, typically by the {@code try}-with-resources |
869 * were suppressed, typically by the {@code try}-with-resources |
809 * statement, in order to deliver this exception. |
870 * statement, in order to deliver this exception. |
|
871 * |
|
872 * If the first exception to be suppressed is {@code null}, that |
|
873 * indicates suppressed exception information will <em>not</em> be |
|
874 * recorded for this exception. Subsequent calls to this method |
|
875 * will not record any suppressed exceptions. Otherwise, |
|
876 * attempting to suppress {@code null} after an exception has |
|
877 * already been successfully suppressed results in a {@code |
|
878 * NullPointerException}. |
810 * |
879 * |
811 * <p>Note that when one exception {@linkplain |
880 * <p>Note that when one exception {@linkplain |
812 * #initCause(Throwable) causes} another exception, the first |
881 * #initCause(Throwable) causes} another exception, the first |
813 * exception is usually caught and then the second exception is |
882 * exception is usually caught and then the second exception is |
814 * thrown in response. In contrast, when one exception suppresses |
883 * thrown in response. In contrast, when one exception suppresses |
817 * control flow can only continue with one exception so the second |
886 * control flow can only continue with one exception so the second |
818 * is recorded as a suppressed exception of the first. |
887 * is recorded as a suppressed exception of the first. |
819 * |
888 * |
820 * @param exception the exception to be added to the list of |
889 * @param exception the exception to be added to the list of |
821 * suppressed exceptions |
890 * suppressed exceptions |
822 * @throws NullPointerException if {@code exception} is null |
|
823 * @throws IllegalArgumentException if {@code exception} is this |
891 * @throws IllegalArgumentException if {@code exception} is this |
824 * throwable; a throwable cannot suppress itself. |
892 * throwable; a throwable cannot suppress itself. |
|
893 * @throws NullPointerException if {@code exception} is null and |
|
894 * an exception has already been suppressed by this exception |
825 * @since 1.7 |
895 * @since 1.7 |
826 */ |
896 */ |
827 public synchronized void addSuppressedException(Throwable exception) { |
897 public final synchronized void addSuppressed(Throwable exception) { |
828 if (exception == null) |
|
829 throw new NullPointerException(NULL_CAUSE_MESSAGE); |
|
830 if (exception == this) |
898 if (exception == this) |
831 throw new IllegalArgumentException("Self-suppression not permitted"); |
899 throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); |
832 |
900 |
833 if (suppressedExceptions == null) |
901 if (exception == null) { |
834 suppressedExceptions = new ArrayList<Throwable>(); |
902 if (suppressedExceptions == SUPPRESSED_SENTINEL) { |
835 suppressedExceptions.add(exception); |
903 suppressedExceptions = null; // No suppression information recorded |
|
904 return; |
|
905 } else |
|
906 throw new NullPointerException(NULL_CAUSE_MESSAGE); |
|
907 } else { |
|
908 assert exception != null && exception != this; |
|
909 |
|
910 if (suppressedExceptions == null) // Suppressed exceptions not recorded |
|
911 return; |
|
912 |
|
913 if (suppressedExceptions == SUPPRESSED_SENTINEL) |
|
914 suppressedExceptions = new ArrayList<Throwable>(1); |
|
915 |
|
916 assert suppressedExceptions != SUPPRESSED_SENTINEL; |
|
917 |
|
918 suppressedExceptions.add(exception); |
|
919 } |
836 } |
920 } |
837 |
921 |
838 private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0]; |
922 private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0]; |
839 |
923 |
840 /** |
924 /** |
841 * Returns an array containing all of the exceptions that were |
925 * Returns an array containing all of the exceptions that were |
842 * suppressed, typically by the {@code try}-with-resources |
926 * suppressed, typically by the {@code try}-with-resources |
843 * statement, in order to deliver this exception. |
927 * statement, in order to deliver this exception. |
844 * |
928 * |
|
929 * If no exceptions were suppressed, an empty array is returned. |
|
930 * |
845 * @return an array containing all of the exceptions that were |
931 * @return an array containing all of the exceptions that were |
846 * suppressed to deliver this exception. |
932 * suppressed to deliver this exception. |
847 * @since 1.7 |
933 * @since 1.7 |
848 */ |
934 */ |
849 public synchronized Throwable[] getSuppressedExceptions() { |
935 public final synchronized Throwable[] getSuppressed() { |
850 if (suppressedExceptions == null) |
936 if (suppressedExceptions == SUPPRESSED_SENTINEL || |
|
937 suppressedExceptions == null) |
851 return EMPTY_THROWABLE_ARRAY; |
938 return EMPTY_THROWABLE_ARRAY; |
852 else |
939 else |
853 return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); |
940 return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); |
854 } |
941 } |
855 } |
942 } |