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