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