001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.exception; 018 019import java.io.PrintStream; 020import java.io.PrintWriter; 021import java.io.StringWriter; 022import java.lang.reflect.Method; 023import java.lang.reflect.UndeclaredThrowableException; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.IdentityHashMap; 027import java.util.List; 028import java.util.Objects; 029import java.util.Set; 030import java.util.StringTokenizer; 031import java.util.function.Consumer; 032import java.util.stream.Stream; 033 034import org.apache.commons.lang3.ArrayUtils; 035import org.apache.commons.lang3.ClassUtils; 036import org.apache.commons.lang3.StringUtils; 037import org.apache.commons.lang3.reflect.MethodUtils; 038import org.apache.commons.lang3.util.IterableStringTokenizer; 039 040/** 041 * Provides utilities for manipulating and examining 042 * {@link Throwable} objects. 043 * 044 * @since 1.0 045 */ 046public class ExceptionUtils { 047 048 /** 049 * The names of methods commonly used to access a wrapped exception. 050 */ 051 // TODO: Remove in Lang 4 052 private static final String[] CAUSE_METHOD_NAMES = { 053 "getCause", 054 "getNextException", 055 "getTargetException", 056 "getException", 057 "getSourceException", 058 "getRootCause", 059 "getCausedByException", 060 "getNested", 061 "getLinkedException", 062 "getNestedException", 063 "getLinkedCause", 064 "getThrowable", 065 }; 066 067 private static final int NOT_FOUND = -1; 068 069 /** 070 * Used when printing stack frames to denote the start of a 071 * wrapped exception. 072 * 073 * <p> 074 * Package private for accessibility by test suite. 075 * </p> 076 */ 077 static final String WRAPPED_MARKER = " [wrapped] "; 078 079 /** 080 * Throws the given (usually checked) exception without adding the exception to the throws 081 * clause of the calling method. This method prevents throws clause 082 * inflation and reduces the clutter of "Caused by" exceptions in the 083 * stack trace. 084 * <p> 085 * The use of this technique may be controversial, but useful. 086 * </p> 087 * <pre> 088 * // There is no throws clause in the method signature. 089 * public int propagateExample { 090 * try { 091 * // Throws IOException 092 * invocation(); 093 * } catch (Exception e) { 094 * // Propagates a checked exception. 095 * throw ExceptionUtils.asRuntimeException(e); 096 * } 097 * // more processing 098 * ... 099 * 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}