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