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