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