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