View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.exception;
18  
19  import java.io.PrintStream;
20  import java.io.PrintWriter;
21  import java.io.StringWriter;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.lang.reflect.UndeclaredThrowableException;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Objects;
28  import java.util.StringTokenizer;
29  
30  import org.apache.commons.lang3.ArrayUtils;
31  import org.apache.commons.lang3.ClassUtils;
32  import org.apache.commons.lang3.StringUtils;
33  
34  /**
35   * <p>Provides utilities for manipulating and examining
36   * {@code Throwable} objects.</p>
37   *
38   * @since 1.0
39   */
40  public class ExceptionUtils {
41  
42      private static final int NOT_FOUND = -1;
43  
44      /**
45       * <p>The names of methods commonly used to access a wrapped exception.</p>
46       */
47      // TODO: Remove in Lang 4.0
48      private static final String[] CAUSE_METHOD_NAMES = {
49          "getCause",
50          "getNextException",
51          "getTargetException",
52          "getException",
53          "getSourceException",
54          "getRootCause",
55          "getCausedByException",
56          "getNested",
57          "getLinkedException",
58          "getNestedException",
59          "getLinkedCause",
60          "getThrowable",
61      };
62  
63      /**
64       * <p>Used when printing stack frames to denote the start of a
65       * wrapped exception.</p>
66       *
67       * <p>Package private for accessibility by test suite.</p>
68       */
69      static final String WRAPPED_MARKER = " [wrapped] ";
70  
71      //-----------------------------------------------------------------------
72      /**
73       * <p>Introspects the {@code Throwable} to obtain the cause.</p>
74       *
75       * <p>The method searches for methods with specific names that return a
76       * {@code Throwable} object. This will pick up most wrapping exceptions,
77       * including those from JDK 1.4.
78       *
79       * <p>The default list searched for are:</p>
80       * <ul>
81       *  <li>{@code getCause()}</li>
82       *  <li>{@code getNextException()}</li>
83       *  <li>{@code getTargetException()}</li>
84       *  <li>{@code getException()}</li>
85       *  <li>{@code getSourceException()}</li>
86       *  <li>{@code getRootCause()}</li>
87       *  <li>{@code getCausedByException()}</li>
88       *  <li>{@code getNested()}</li>
89       * </ul>
90       *
91       * <p>If none of the above is found, returns {@code null}.</p>
92       *
93       * @param throwable  the throwable to introspect for a cause, may be null
94       * @return the cause of the {@code Throwable},
95       *  {@code null} if none found or null throwable input
96       * @since 1.0
97       * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead
98       */
99      @Deprecated
100     public static Throwable getCause(final Throwable throwable) {
101         return getCause(throwable, null);
102     }
103 
104     /**
105      * <p>Introspects the {@code Throwable} to obtain the cause.</p>
106      *
107      * <p>A {@code null} set of method names means use the default set.
108      * A {@code null} in the set of method names will be ignored.</p>
109      *
110      * @param throwable  the throwable to introspect for a cause, may be null
111      * @param methodNames  the method names, null treated as default set
112      * @return the cause of the {@code Throwable},
113      *  {@code null} if none found or null throwable input
114      * @since 1.0
115      * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead
116      */
117     @Deprecated
118     public static Throwable getCause(final Throwable throwable, String[] methodNames) {
119         if (throwable == null) {
120             return null;
121         }
122 
123         if (methodNames == null) {
124             final Throwable cause = throwable.getCause();
125             if (cause != null) {
126                 return cause;
127             }
128 
129             methodNames = CAUSE_METHOD_NAMES;
130         }
131 
132         for (final String methodName : methodNames) {
133             if (methodName != null) {
134                 final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName);
135                 if (legacyCause != null) {
136                     return legacyCause;
137                 }
138             }
139         }
140 
141         return null;
142     }
143 
144     /**
145      * <p>Finds a {@code Throwable} by method name.</p>
146      *
147      * @param throwable  the exception to examine
148      * @param methodName  the name of the method to find and invoke
149      * @return the wrapped exception, or {@code null} if not found
150      */
151     // TODO: Remove in Lang 4.0
152     private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) {
153         Method method = null;
154         try {
155             method = throwable.getClass().getMethod(methodName);
156         } catch (final NoSuchMethodException | SecurityException ignored) { // NOPMD
157             // exception ignored
158         }
159 
160         if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
161             try {
162                 return (Throwable) method.invoke(throwable);
163             } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) { // NOPMD
164                 // exception ignored
165             }
166         }
167         return null;
168     }
169 
170     //-----------------------------------------------------------------------
171     /**
172      * <p>Returns the default names used when searching for the cause of an exception.</p>
173      *
174      * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
175      *
176      * @return cloned array of the default method names
177      * @since 3.0
178      * @deprecated This feature will be removed in Lang 4.0
179      */
180     @Deprecated
181     public static String[] getDefaultCauseMethodNames() {
182         return ArrayUtils.clone(CAUSE_METHOD_NAMES);
183     }
184 
185     //-----------------------------------------------------------------------
186     /**
187      * Gets a short message summarising the exception.
188      * <p>
189      * The message returned is of the form
190      * {ClassNameWithoutPackage}: {ThrowableMessage}
191      *
192      * @param th  the throwable to get a message for, null returns empty string
193      * @return the message, non-null
194      * @since 2.2
195      */
196     public static String getMessage(final Throwable th) {
197         if (th == null) {
198             return StringUtils.EMPTY;
199         }
200         final String clsName = ClassUtils.getShortClassName(th, null);
201         final String msg = th.getMessage();
202         return clsName + ": " + StringUtils.defaultString(msg);
203     }
204 
205     /**
206      * <p>Introspects the {@code Throwable} to obtain the root cause.</p>
207      *
208      * <p>This method walks through the exception chain to the last element,
209      * "root" of the tree, using {@link Throwable#getCause()}, and
210      * returns that exception.</p>
211      *
212      * <p>From version 2.2, this method handles recursive cause structures
213      * that might otherwise cause infinite loops. If the throwable parameter
214      * has a cause of itself, then null will be returned. If the throwable
215      * parameter cause chain loops, the last element in the chain before the
216      * loop is returned.</p>
217      *
218      * @param throwable  the throwable to get the root cause for, may be null
219      * @return the root cause of the {@code Throwable},
220      *  {@code null} if null throwable input
221      */
222     public static Throwable getRootCause(final Throwable throwable) {
223         final List<Throwable> list = getThrowableList(throwable);
224         return list.isEmpty() ? null : list.get(list.size() - 1);
225     }
226 
227     //-----------------------------------------------------------------------
228     /**
229      * Gets a short message summarising the root cause exception.
230      * <p>
231      * The message returned is of the form
232      * {ClassNameWithoutPackage}: {ThrowableMessage}
233      *
234      * @param th  the throwable to get a message for, null returns empty string
235      * @return the message, non-null
236      * @since 2.2
237      */
238     public static String getRootCauseMessage(final Throwable th) {
239         Throwable root = getRootCause(th);
240         root = root == null ? th : root;
241         return getMessage(root);
242     }
243 
244     //-----------------------------------------------------------------------
245     /**
246      * <p>Creates a compact stack trace for the root cause of the supplied
247      * {@code Throwable}.</p>
248      *
249      * <p>The output of this method is consistent across JDK versions.
250      * It consists of the root exception followed by each of its wrapping
251      * exceptions separated by '[wrapped]'. Note that this is the opposite
252      * order to the JDK1.4 display.</p>
253      *
254      * @param throwable  the throwable to examine, may be null
255      * @return an array of stack trace frames, never null
256      * @since 2.0
257      */
258     public static String[] getRootCauseStackTrace(final Throwable throwable) {
259         if (throwable == null) {
260             return ArrayUtils.EMPTY_STRING_ARRAY;
261         }
262         final Throwable[] throwables = getThrowables(throwable);
263         final int count = throwables.length;
264         final List<String> frames = new ArrayList<>();
265         List<String> nextTrace = getStackFrameList(throwables[count - 1]);
266         for (int i = count; --i >= 0;) {
267             final List<String> trace = nextTrace;
268             if (i != 0) {
269                 nextTrace = getStackFrameList(throwables[i - 1]);
270                 removeCommonFrames(trace, nextTrace);
271             }
272             if (i == count - 1) {
273                 frames.add(throwables[i].toString());
274             } else {
275                 frames.add(WRAPPED_MARKER + throwables[i].toString());
276             }
277             frames.addAll(trace);
278         }
279         return frames.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
280     }
281 
282     /**
283      * <p>Produces a {@code List} of stack frames - the message
284      * is not included. Only the trace of the specified exception is
285      * returned, any caused by trace is stripped.</p>
286      *
287      * <p>This works in most cases - it will only fail if the exception
288      * message contains a line that starts with:
289      * {@code &quot;&nbsp;&nbsp;&nbsp;at&quot;.}</p>
290      *
291      * @param t is any throwable
292      * @return List of stack frames
293      */
294     static List<String> getStackFrameList(final Throwable t) {
295         final String stackTrace = getStackTrace(t);
296         final String linebreak = System.lineSeparator();
297         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
298         final List<String> list = new ArrayList<>();
299         boolean traceStarted = false;
300         while (frames.hasMoreTokens()) {
301             final String token = frames.nextToken();
302             // Determine if the line starts with <whitespace>at
303             final int at = token.indexOf("at");
304             if (at != NOT_FOUND && token.substring(0, at).trim().isEmpty()) {
305                 traceStarted = true;
306                 list.add(token);
307             } else if (traceStarted) {
308                 break;
309             }
310         }
311         return list;
312     }
313 
314     //-----------------------------------------------------------------------
315     /**
316      * <p>Returns an array where each element is a line from the argument.</p>
317      *
318      * <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p>
319      *
320      * @param stackTrace  a stack trace String
321      * @return an array where each element is a line from the argument
322      */
323     static String[] getStackFrames(final String stackTrace) {
324         final String linebreak = System.lineSeparator();
325         final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
326         final List<String> list = new ArrayList<>();
327         while (frames.hasMoreTokens()) {
328             list.add(frames.nextToken());
329         }
330         return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
331     }
332 
333     /**
334      * <p>Captures the stack trace associated with the specified
335      * {@code Throwable} object, decomposing it into a list of
336      * stack frames.</p>
337      *
338      * <p>The result of this method vary by JDK version as this method
339      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
340      * On JDK1.3 and earlier, the cause exception will not be shown
341      * unless the specified throwable alters printStackTrace.</p>
342      *
343      * @param throwable  the {@code Throwable} to examine, may be null
344      * @return an array of strings describing each stack frame, never null
345      */
346     public static String[] getStackFrames(final Throwable throwable) {
347         if (throwable == null) {
348             return ArrayUtils.EMPTY_STRING_ARRAY;
349         }
350         return getStackFrames(getStackTrace(throwable));
351     }
352 
353     //-----------------------------------------------------------------------
354     /**
355      * <p>Gets the stack trace from a Throwable as a String.</p>
356      *
357      * <p>The result of this method vary by JDK version as this method
358      * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
359      * On JDK1.3 and earlier, the cause exception will not be shown
360      * unless the specified throwable alters printStackTrace.</p>
361      *
362      * @param throwable  the {@code Throwable} to be examined
363      * @return the stack trace as generated by the exception's
364      *  {@code printStackTrace(PrintWriter)} method
365      */
366     public static String getStackTrace(final Throwable throwable) {
367         final StringWriter sw = new StringWriter();
368         final PrintWriter pw = new PrintWriter(sw, true);
369         throwable.printStackTrace(pw);
370         return sw.getBuffer().toString();
371     }
372 
373     //-----------------------------------------------------------------------
374     /**
375      * <p>Counts the number of {@code Throwable} objects in the
376      * exception chain.</p>
377      *
378      * <p>A throwable without cause will return {@code 1}.
379      * A throwable with one cause will return {@code 2} and so on.
380      * A {@code null} throwable will return {@code 0}.</p>
381      *
382      * <p>From version 2.2, this method handles recursive cause structures
383      * that might otherwise cause infinite loops. The cause chain is
384      * processed until the end is reached, or until the next item in the
385      * chain is already in the result set.</p>
386      *
387      * @param throwable  the throwable to inspect, may be null
388      * @return the count of throwables, zero if null input
389      */
390     public static int getThrowableCount(final Throwable throwable) {
391         return getThrowableList(throwable).size();
392     }
393 
394     /**
395      * <p>Returns the list of {@code Throwable} objects in the
396      * exception chain.</p>
397      *
398      * <p>A throwable without cause will return a list containing
399      * one element - the input throwable.
400      * A throwable with one cause will return a list containing
401      * two elements. - the input throwable and the cause throwable.
402      * A {@code null} throwable will return a list of size zero.</p>
403      *
404      * <p>This method handles recursive cause structures that might
405      * otherwise cause infinite loops. The cause chain is processed until
406      * the end is reached, or until the next item in the chain is already
407      * in the result set.</p>
408      *
409      * @param throwable  the throwable to inspect, may be null
410      * @return the list of throwables, never null
411      * @since 2.2
412      */
413     public static List<Throwable> getThrowableList(Throwable throwable) {
414         final List<Throwable> list = new ArrayList<>();
415         while (throwable != null && !list.contains(throwable)) {
416             list.add(throwable);
417             throwable = throwable.getCause();
418         }
419         return list;
420     }
421 
422     /**
423      * <p>Returns the list of {@code Throwable} objects in the
424      * exception chain.</p>
425      *
426      * <p>A throwable without cause will return an array containing
427      * one element - the input throwable.
428      * A throwable with one cause will return an array containing
429      * two elements. - the input throwable and the cause throwable.
430      * A {@code null} throwable will return an array of size zero.</p>
431      *
432      * <p>From version 2.2, this method handles recursive cause structures
433      * that might otherwise cause infinite loops. The cause chain is
434      * processed until the end is reached, or until the next item in the
435      * chain is already in the result set.</p>
436      *
437      * @see #getThrowableList(Throwable)
438      * @param throwable  the throwable to inspect, may be null
439      * @return the array of throwables, never null
440      */
441     public static Throwable[] getThrowables(final Throwable throwable) {
442         final List<Throwable> list = getThrowableList(throwable);
443         return list.toArray(ArrayUtils.EMPTY_THROWABLE_ARRAY);
444     }
445 
446     /**
447      * Does the throwable's causal chain have an immediate or wrapped exception
448      * of the given type?
449      *
450      * @param chain
451      *            The root of a Throwable causal chain.
452      * @param type
453      *            The exception type to test.
454      * @return true, if chain is an instance of type or is an
455      *         UndeclaredThrowableException wrapping a cause.
456      * @since 3.5
457      * @see #wrapAndThrow(Throwable)
458      */
459     public static boolean hasCause(Throwable chain,
460             final Class<? extends Throwable> type) {
461         if (chain instanceof UndeclaredThrowableException) {
462             chain = chain.getCause();
463         }
464         return type.isInstance(chain);
465     }
466 
467     /**
468      * <p>Worker method for the {@code indexOfType} methods.</p>
469      *
470      * @param throwable  the throwable to inspect, may be null
471      * @param type  the type to search for, subclasses match, null returns -1
472      * @param fromIndex  the (zero-based) index of the starting position,
473      *  negative treated as zero, larger than chain size returns -1
474      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
475      * using references
476      * @return index of the {@code type} within throwables nested within the specified {@code throwable}
477      */
478     private static int indexOf(final Throwable throwable, final Class<? extends Throwable> type, int fromIndex, final boolean subclass) {
479         if (throwable == null || type == null) {
480             return NOT_FOUND;
481         }
482         if (fromIndex < 0) {
483             fromIndex = 0;
484         }
485         final Throwable[] throwables = getThrowables(throwable);
486         if (fromIndex >= throwables.length) {
487             return NOT_FOUND;
488         }
489         if (subclass) {
490             for (int i = fromIndex; i < throwables.length; i++) {
491                 if (type.isAssignableFrom(throwables[i].getClass())) {
492                     return i;
493                 }
494             }
495         } else {
496             for (int i = fromIndex; i < throwables.length; i++) {
497                 if (type.equals(throwables[i].getClass())) {
498                     return i;
499                 }
500             }
501         }
502         return NOT_FOUND;
503     }
504 
505     /**
506      * <p>Returns the (zero-based) index of the first {@code Throwable}
507      * that matches the specified class (exactly) in the exception chain.
508      * Subclasses of the specified class do not match - see
509      * {@link #indexOfType(Throwable, Class)} for the opposite.</p>
510      *
511      * <p>A {@code null} throwable returns {@code -1}.
512      * A {@code null} type returns {@code -1}.
513      * No match in the chain returns {@code -1}.</p>
514      *
515      * @param throwable  the throwable to inspect, may be null
516      * @param clazz  the class to search for, subclasses do not match, null returns -1
517      * @return the index into the throwable chain, -1 if no match or null input
518      */
519     public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz) {
520         return indexOf(throwable, clazz, 0, false);
521     }
522 
523     /**
524      * <p>Returns the (zero-based) index of the first {@code Throwable}
525      * that matches the specified type in the exception chain from
526      * a specified index.
527      * Subclasses of the specified class do not match - see
528      * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p>
529      *
530      * <p>A {@code null} throwable returns {@code -1}.
531      * A {@code null} type returns {@code -1}.
532      * No match in the chain returns {@code -1}.
533      * A negative start index is treated as zero.
534      * A start index greater than the number of throwables returns {@code -1}.</p>
535      *
536      * @param throwable  the throwable to inspect, may be null
537      * @param clazz  the class to search for, subclasses do not match, null returns -1
538      * @param fromIndex  the (zero-based) index of the starting position,
539      *  negative treated as zero, larger than chain size returns -1
540      * @return the index into the throwable chain, -1 if no match or null input
541      */
542     public static int indexOfThrowable(final Throwable throwable, final Class<? extends Throwable> clazz, final int fromIndex) {
543         return indexOf(throwable, clazz, fromIndex, false);
544     }
545 
546     /**
547      * <p>Returns the (zero-based) index of the first {@code Throwable}
548      * that matches the specified class or subclass in the exception chain.
549      * Subclasses of the specified class do match - see
550      * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
551      *
552      * <p>A {@code null} throwable returns {@code -1}.
553      * A {@code null} type returns {@code -1}.
554      * No match in the chain returns {@code -1}.</p>
555      *
556      * @param throwable  the throwable to inspect, may be null
557      * @param type  the type to search for, subclasses match, null returns -1
558      * @return the index into the throwable chain, -1 if no match or null input
559      * @since 2.1
560      */
561     public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type) {
562         return indexOf(throwable, type, 0, true);
563     }
564 
565     /**
566      * <p>Returns the (zero-based) index of the first {@code Throwable}
567      * that matches the specified type in the exception chain from
568      * a specified index.
569      * Subclasses of the specified class do match - see
570      * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
571      *
572      * <p>A {@code null} throwable returns {@code -1}.
573      * A {@code null} type returns {@code -1}.
574      * No match in the chain returns {@code -1}.
575      * A negative start index is treated as zero.
576      * A start index greater than the number of throwables returns {@code -1}.</p>
577      *
578      * @param throwable  the throwable to inspect, may be null
579      * @param type  the type to search for, subclasses match, null returns -1
580      * @param fromIndex  the (zero-based) index of the starting position,
581      *  negative treated as zero, larger than chain size returns -1
582      * @return the index into the throwable chain, -1 if no match or null input
583      * @since 2.1
584      */
585     public static int indexOfType(final Throwable throwable, final Class<? extends Throwable> type, final int fromIndex) {
586         return indexOf(throwable, type, fromIndex, true);
587     }
588 
589     //-----------------------------------------------------------------------
590     /**
591      * <p>Prints a compact stack trace for the root cause of a throwable
592      * to {@code System.err}.</p>
593      *
594      * <p>The compact stack trace starts with the root cause and prints
595      * stack frames up to the place where it was caught and wrapped.
596      * Then it prints the wrapped exception and continues with stack frames
597      * until the wrapper exception is caught and wrapped again, etc.</p>
598      *
599      * <p>The output of this method is consistent across JDK versions.
600      * Note that this is the opposite order to the JDK1.4 display.</p>
601      *
602      * <p>The method is equivalent to {@code printStackTrace} for throwables
603      * that don't have nested causes.</p>
604      *
605      * @param throwable  the throwable to output
606      * @since 2.0
607      */
608     public static void printRootCauseStackTrace(final Throwable throwable) {
609         printRootCauseStackTrace(throwable, System.err);
610     }
611 
612     /**
613      * <p>Prints a compact stack trace for the root cause of a throwable.</p>
614      *
615      * <p>The compact stack trace starts with the root cause and prints
616      * stack frames up to the place where it was caught and wrapped.
617      * Then it prints the wrapped exception and continues with stack frames
618      * until the wrapper exception is caught and wrapped again, etc.</p>
619      *
620      * <p>The output of this method is consistent across JDK versions.
621      * Note that this is the opposite order to the JDK1.4 display.</p>
622      *
623      * <p>The method is equivalent to {@code printStackTrace} for throwables
624      * that don't have nested causes.</p>
625      *
626      * @param throwable  the throwable to output, may be null
627      * @param printStream  the stream to output to, may not be null
628      * @throws NullPointerException if the printStream is {@code null}
629      * @since 2.0
630      */
631     @SuppressWarnings("resource")
632     public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) {
633         if (throwable == null) {
634             return;
635         }
636         Objects.requireNonNull(printStream, "printStream");
637         final String[] trace = getRootCauseStackTrace(throwable);
638         for (final String element : trace) {
639             printStream.println(element);
640         }
641         printStream.flush();
642     }
643 
644     /**
645      * <p>Prints a compact stack trace for the root cause of a throwable.</p>
646      *
647      * <p>The compact stack trace starts with the root cause and prints
648      * stack frames up to the place where it was caught and wrapped.
649      * Then it prints the wrapped exception and continues with stack frames
650      * until the wrapper exception is caught and wrapped again, etc.</p>
651      *
652      * <p>The output of this method is consistent across JDK versions.
653      * Note that this is the opposite order to the JDK1.4 display.</p>
654      *
655      * <p>The method is equivalent to {@code printStackTrace} for throwables
656      * that don't have nested causes.</p>
657      *
658      * @param throwable  the throwable to output, may be null
659      * @param printWriter  the writer to output to, may not be null
660      * @throws NullPointerException if the printWriter is {@code null}
661      * @since 2.0
662      */
663     @SuppressWarnings("resource")
664     public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) {
665         if (throwable == null) {
666             return;
667         }
668         Objects.requireNonNull(printWriter, "printWriter");
669         final String[] trace = getRootCauseStackTrace(throwable);
670         for (final String element : trace) {
671             printWriter.println(element);
672         }
673         printWriter.flush();
674     }
675 
676     /**
677      * <p>Removes common frames from the cause trace given the two stack traces.</p>
678      *
679      * @param causeFrames  stack trace of a cause throwable
680      * @param wrapperFrames  stack trace of a wrapper throwable
681      * @throws IllegalArgumentException if either argument is null
682      * @since 2.0
683      */
684     public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {
685         if (causeFrames == null || wrapperFrames == null) {
686             throw new IllegalArgumentException("The List must not be null");
687         }
688         int causeFrameIndex = causeFrames.size() - 1;
689         int wrapperFrameIndex = wrapperFrames.size() - 1;
690         while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
691             // Remove the frame from the cause trace if it is the same
692             // as in the wrapper trace
693             final String causeFrame = causeFrames.get(causeFrameIndex);
694             final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
695             if (causeFrame.equals(wrapperFrame)) {
696                 causeFrames.remove(causeFrameIndex);
697             }
698             causeFrameIndex--;
699             wrapperFrameIndex--;
700         }
701     }
702 
703     /**
704      * Throw a checked exception without adding the exception to the throws
705      * clause of the calling method. This method prevents throws clause
706      * pollution and reduces the clutter of "Caused by" exceptions in the
707      * stacktrace.
708      * <p>
709      * The use of this technique may be controversial, but exceedingly useful to
710      * library developers.
711      * <code>
712      *  public int propagateExample { // note that there is no throws clause
713      *      try {
714      *          return invocation(); // throws IOException
715      *      } catch (Exception e) {
716      *          return ExceptionUtils.rethrow(e);  // propagates a checked exception
717      *      }
718      *  }
719      * </code>
720      * <p>
721      * This is an alternative to the more conservative approach of wrapping the
722      * checked exception in a RuntimeException:
723      * <code>
724      *  public int wrapExample { // note that there is no throws clause
725      *      try {
726      *          return invocation(); // throws IOException
727      *      } catch (Error e) {
728      *          throw e;
729      *      } catch (RuntimeException e) {
730      *          throw e;  // wraps a checked exception
731      *      } catch (Exception e) {
732      *          throw new UndeclaredThrowableException(e);  // wraps a checked exception
733      *      }
734      *  }
735      * </code>
736      * <p>
737      * One downside to using this approach is that the java compiler will not
738      * allow invoking code to specify a checked exception in a catch clause
739      * unless there is some code path within the try block that has invoked a
740      * method declared with that checked exception. If the invoking site wishes
741      * to catch the shaded checked exception, it must either invoke the shaded
742      * code through a method re-declaring the desired checked exception, or
743      * catch Exception and use the instanceof operator. Either of these
744      * techniques are required when interacting with non-java jvm code such as
745      * Jython, Scala, or Groovy, since these languages do not consider any
746      * exceptions as checked.
747      *
748      * @param throwable
749      *            The throwable to rethrow.
750      * @param <R> The type of the returned value.
751      * @return Never actually returned, this generic type matches any type
752      *         which the calling site requires. "Returning" the results of this
753      *         method, as done in the propagateExample above, will satisfy the
754      *         java compiler requirement that all code paths return a value.
755      * @since 3.5
756      * @see #wrapAndThrow(Throwable)
757      */
758     public static <R> R rethrow(final Throwable throwable) {
759         // claim that the typeErasure invocation throws a RuntimeException
760         return ExceptionUtils.<R, RuntimeException>typeErasure(throwable);
761     }
762 
763     /**
764      * <p>Worker method for the {@code throwableOfType} methods.</p>
765      *
766      * @param <T> the type of Throwable you are searching.
767      * @param throwable  the throwable to inspect, may be null
768      * @param type  the type to search, subclasses match, null returns null
769      * @param fromIndex  the (zero-based) index of the starting position,
770      *  negative treated as zero, larger than chain size returns null
771      * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
772      * using references
773      * @return throwable of the {@code type} within throwables nested within the specified {@code throwable}
774      */
775     private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) {
776         if (throwable == null || type == null) {
777             return null;
778         }
779         if (fromIndex < 0) {
780             fromIndex = 0;
781         }
782         final Throwable[] throwables = getThrowables(throwable);
783         if (fromIndex >= throwables.length) {
784             return null;
785         }
786         if (subclass) {
787             for (int i = fromIndex; i < throwables.length; i++) {
788                 if (type.isAssignableFrom(throwables[i].getClass())) {
789                     return type.cast(throwables[i]);
790                 }
791             }
792         } else {
793             for (int i = fromIndex; i < throwables.length; i++) {
794                 if (type.equals(throwables[i].getClass())) {
795                     return type.cast(throwables[i]);
796                 }
797             }
798         }
799         return null;
800     }
801 
802     /**
803      * <p>Returns the first {@code Throwable}
804      * that matches the specified class (exactly) in the exception chain.
805      * Subclasses of the specified class do not match - see
806      * {@link #throwableOfType(Throwable, Class)} for the opposite.</p>
807      *
808      * <p>A {@code null} throwable returns {@code null}.
809      * A {@code null} type returns {@code null}.
810      * No match in the chain returns {@code null}.</p>
811      *
812      * @param <T> the type of Throwable you are searching.
813      * @param throwable  the throwable to inspect, may be null
814      * @param clazz  the class to search for, subclasses do not match, null returns null
815      * @return the first matching throwable from the throwable chain, null if no match or null input
816      * @since 3.10
817      */
818     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) {
819         return throwableOf(throwable, clazz, 0, false);
820     }
821 
822     /**
823      * <p>Returns the first {@code Throwable}
824      * that matches the specified type in the exception chain from
825      * a specified index.
826      * Subclasses of the specified class do not match - see
827      * {@link #throwableOfType(Throwable, Class, int)} for the opposite.</p>
828      *
829      * <p>A {@code null} throwable returns {@code null}.
830      * A {@code null} type returns {@code null}.
831      * No match in the chain returns {@code null}.
832      * A negative start index is treated as zero.
833      * A start index greater than the number of throwables returns {@code null}.</p>
834      *
835      * @param <T> the type of Throwable you are searching.
836      * @param throwable  the throwable to inspect, may be null
837      * @param clazz  the class to search for, subclasses do not match, null returns null
838      * @param fromIndex  the (zero-based) index of the starting position,
839      *  negative treated as zero, larger than chain size returns null
840      * @return the first matching throwable from the throwable chain, null if no match or null input
841      * @since 3.10
842      */
843     public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) {
844         return throwableOf(throwable, clazz, fromIndex, false);
845     }
846 
847     /**
848      * <p>Returns the throwable of the first {@code Throwable}
849      * that matches the specified class or subclass in the exception chain.
850      * Subclasses of the specified class do match - see
851      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite..</p>
852      *
853      * <p>A {@code null} throwable returns {@code null}.
854      * A {@code null} type returns {@code null}.
855      * No match in the chain returns {@code null}.</p>
856      *
857      * @param <T> the type of Throwable you are searching.
858      * @param throwable  the throwable to inspect, may be null
859      * @param type  the type to search for, subclasses match, null returns null
860      * @return the first matching throwable from the throwable chain, null if no match or null input
861      * @since 3.10
862      */
863     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) {
864         return throwableOf(throwable, type, 0, true);
865     }
866 
867     /**
868      * <p>Returns the first {@code Throwable}
869      * that matches the specified type in the exception chain from
870      * a specified index.
871      * Subclasses of the specified class do match - see
872      * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.</p>
873      *
874      * <p>A {@code null} throwable returns {@code null}.
875      * A {@code null} type returns {@code null}.
876      * No match in the chain returns {@code null}.
877      * A negative start index is treated as zero.
878      * A start index greater than the number of throwables returns {@code null}.</p>
879      *
880      * @param <T> the type of Throwable you are searching.
881      * @param throwable  the throwable to inspect, may be null
882      * @param type  the type to search for, subclasses match, null returns null
883      * @param fromIndex  the (zero-based) index of the starting position,
884      *  negative treated as zero, larger than chain size returns null
885      * @return the first matching throwable from the throwable chain, null if no match or null input
886      * @since 3.10
887      */
888     public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) {
889         return throwableOf(throwable, type, fromIndex, true);
890     }
891 
892     /**
893      * Claim a Throwable is another Exception type using type erasure. This
894      * hides a checked exception from the java compiler, allowing a checked
895      * exception to be thrown without having the exception in the method's throw
896      * clause.
897      */
898     @SuppressWarnings("unchecked")
899     private static <R, T extends Throwable> R typeErasure(final Throwable throwable) throws T {
900         throw (T) throwable;
901     }
902 
903     /**
904      * Throw a checked exception without adding the exception to the throws
905      * clause of the calling method. For checked exceptions, this method throws
906      * an UndeclaredThrowableException wrapping the checked exception. For
907      * Errors and RuntimeExceptions, the original exception is rethrown.
908      * <p>
909      * The downside to using this approach is that invoking code which needs to
910      * handle specific checked exceptions must sniff up the exception chain to
911      * determine if the caught exception was caused by the checked exception.
912      *
913      * @param throwable
914      *            The throwable to rethrow.
915      * @param <R> The type of the returned value.
916      * @return Never actually returned, this generic type matches any type
917      *         which the calling site requires. "Returning" the results of this
918      *         method will satisfy the java compiler requirement that all code
919      *         paths return a value.
920      * @since 3.5
921      * @see #rethrow(Throwable)
922      * @see #hasCause(Throwable, Class)
923      */
924     public static <R> R wrapAndThrow(final Throwable throwable) {
925         if (throwable instanceof RuntimeException) {
926             throw (RuntimeException) throwable;
927         }
928         if (throwable instanceof Error) {
929             throw (Error) throwable;
930         }
931         throw new UndeclaredThrowableException(throwable);
932     }
933 
934     /**
935      * <p>
936      * Public constructor allows an instance of {@code ExceptionUtils} to be created, although that is not
937      * normally necessary.
938      * </p>
939      */
940     public ExceptionUtils() {
941     }
942 }