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