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.List; 027import java.util.Objects; 028import java.util.StringTokenizer; 029import java.util.function.Consumer; 030import java.util.stream.Stream; 031 032import org.apache.commons.lang3.ArrayUtils; 033import org.apache.commons.lang3.ClassUtils; 034import org.apache.commons.lang3.StringUtils; 035import org.apache.commons.lang3.reflect.MethodUtils; 036import org.apache.commons.lang3.util.IterableStringTokenizer; 037 038/** 039 * Provides utilities for manipulating and examining 040 * {@link Throwable} objects. 041 * 042 * @since 1.0 043 */ 044public class ExceptionUtils { 045 046 /** 047 * The names of methods commonly used to access a wrapped exception. 048 */ 049 // TODO: Remove in Lang 4 050 private static final String[] CAUSE_METHOD_NAMES = { 051 "getCause", 052 "getNextException", 053 "getTargetException", 054 "getException", 055 "getSourceException", 056 "getRootCause", 057 "getCausedByException", 058 "getNested", 059 "getLinkedException", 060 "getNestedException", 061 "getLinkedCause", 062 "getThrowable", 063 }; 064 065 private static final int NOT_FOUND = -1; 066 067 /** 068 * Used when printing stack frames to denote the start of a 069 * wrapped exception. 070 * 071 * <p>Package private for accessibility by test suite.</p> 072 */ 073 static final String WRAPPED_MARKER = " [wrapped] "; 074 075 /** 076 * Throws the given (usually checked) exception without adding the exception to the throws 077 * clause of the calling method. This method prevents throws clause 078 * inflation and reduces the clutter of "Caused by" exceptions in the 079 * stack trace. 080 * <p> 081 * The use of this technique may be controversial, but useful. 082 * </p> 083 * <pre> 084 * // There is no throws clause in the method signature. 085 * public int propagateExample { 086 * try { 087 * // Throws IOException 088 * invocation(); 089 * } catch (Exception e) { 090 * // Propagates a checked exception. 091 * throw ExceptionUtils.asRuntimeException(e); 092 * } 093 * // more processing 094 * ... 095 * return value; 096 * } 097 * </pre> 098 * <p> 099 * This is an alternative to the more conservative approach of wrapping the 100 * checked exception in a RuntimeException: 101 * </p> 102 * <pre> 103 * // There is no throws clause in the method signature. 104 * public int wrapExample() { 105 * try { 106 * // throws IOException. 107 * invocation(); 108 * } catch (Error e) { 109 * throw e; 110 * } catch (RuntimeException e) { 111 * // Throws an unchecked exception. 112 * throw e; 113 * } catch (Exception e) { 114 * // Wraps a checked exception. 115 * throw new UndeclaredThrowableException(e); 116 * } 117 * // more processing 118 * ... 119 * return value; 120 * } 121 * </pre> 122 * <p> 123 * One downside to using this approach is that the Java compiler will not 124 * allow invoking code to specify a checked exception in a catch clause 125 * unless there is some code path within the try block that has invoked a 126 * method declared with that checked exception. If the invoking site wishes 127 * to catch the shaded checked exception, it must either invoke the shaded 128 * code through a method re-declaring the desired checked exception, or 129 * catch Exception and use the {@code instanceof} operator. Either of these 130 * techniques are required when interacting with non-Java JVM code such as 131 * Jython, Scala, or Groovy, since these languages do not consider any 132 * exceptions as checked. 133 * </p> 134 * 135 * @param throwable 136 * The throwable to rethrow. 137 * @param <T> The type of the returned value. 138 * @return Never actually returned, this generic type matches any type 139 * which the calling site requires. "Returning" the results of this 140 * method, as done in the propagateExample above, will satisfy the 141 * Java compiler requirement that all code paths return a value. 142 * @since 3.14.0 143 * @see #wrapAndThrow(Throwable) 144 */ 145 public static <T extends RuntimeException> T asRuntimeException(final Throwable throwable) { 146 // claim that the typeErasure invocation throws a RuntimeException 147 return ExceptionUtils.<T, RuntimeException>eraseType(throwable); 148 } 149 150 /** 151 * Claims a Throwable is another Throwable type using type erasure. This 152 * hides a checked exception from the Java compiler, allowing a checked 153 * exception to be thrown without having the exception in the method's throw 154 * clause. 155 */ 156 @SuppressWarnings("unchecked") 157 private static <R, T extends Throwable> R eraseType(final Throwable throwable) throws T { 158 throw (T) throwable; 159 } 160 161 /** 162 * Performs an action for each Throwable causes of the given Throwable. 163 * <p> 164 * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause 165 * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable 166 * will return a stream of count zero. 167 * </p> 168 * 169 * <p> 170 * This method handles recursive cause structures that might otherwise cause infinite loops. The cause chain is 171 * processed until the end is reached, or until the next item in the chain is already in the result set. 172 * </p> 173 * @param throwable The Throwable to traverse. 174 * @param consumer a non-interfering action to perform on the elements. 175 * @since 3.13.0 176 */ 177 public static void forEach(final Throwable throwable, final Consumer<Throwable> consumer) { 178 stream(throwable).forEach(consumer); 179 } 180 181 /** 182 * Introspects the {@link Throwable} to obtain the cause. 183 * 184 * <p>The method searches for methods with specific names that return a 185 * {@link Throwable} object. This will pick up most wrapping exceptions, 186 * including those from JDK 1.4. 187 * </p> 188 * 189 * <p>The default list searched for are:</p> 190 * <ul> 191 * <li>{@code getCause()}</li> 192 * <li>{@code getNextException()}</li> 193 * <li>{@code getTargetException()}</li> 194 * <li>{@code getException()}</li> 195 * <li>{@code getSourceException()}</li> 196 * <li>{@code getRootCause()}</li> 197 * <li>{@code getCausedByException()}</li> 198 * <li>{@code getNested()}</li> 199 * </ul> 200 * 201 * <p>If none of the above is found, returns {@code null}.</p> 202 * 203 * @param throwable the throwable to introspect for a cause, may be null 204 * @return the cause of the {@link Throwable}, 205 * {@code null} if none found or null throwable input 206 * @since 1.0 207 * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead 208 */ 209 @Deprecated 210 public static Throwable getCause(final Throwable throwable) { 211 return getCause(throwable, null); 212 } 213 214 /** 215 * Introspects the {@link Throwable} to obtain the cause. 216 * 217 * <p>A {@code null} set of method names means use the default set. 218 * A {@code null} in the set of method names will be ignored.</p> 219 * 220 * @param throwable the throwable to introspect for a cause, may be null 221 * @param methodNames the method names, null treated as default set 222 * @return the cause of the {@link Throwable}, 223 * {@code null} if none found or null throwable input 224 * @since 1.0 225 * @deprecated This feature will be removed in Lang 4, use {@link Throwable#getCause} instead 226 */ 227 @Deprecated 228 public static Throwable getCause(final Throwable throwable, String[] methodNames) { 229 if (throwable == null) { 230 return null; 231 } 232 if (methodNames == null) { 233 final Throwable cause = throwable.getCause(); 234 if (cause != null) { 235 return cause; 236 } 237 methodNames = CAUSE_METHOD_NAMES; 238 } 239 return Stream.of(methodNames).map(m -> getCauseUsingMethodName(throwable, m)).filter(Objects::nonNull).findFirst().orElse(null); 240 } 241 242 /** 243 * Gets a {@link Throwable} by method name. 244 * 245 * @param throwable the exception to examine 246 * @param methodName the name of the method to find and invoke 247 * @return the wrapped exception, or {@code null} if not found 248 */ 249 // TODO: Remove in Lang 4 250 private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { 251 if (methodName != null) { 252 final Method method = MethodUtils.getMethodObject(throwable.getClass(), methodName); 253 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { 254 try { 255 return (Throwable) method.invoke(throwable); 256 } catch (final ReflectiveOperationException ignored) { 257 // exception ignored 258 } 259 } 260 } 261 return null; 262 } 263 264 /** 265 * Gets the default names used when searching for the cause of an exception. 266 * 267 * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p> 268 * 269 * @return cloned array of the default method names 270 * @since 3.0 271 * @deprecated This feature will be removed in Lang 4 272 */ 273 @Deprecated 274 public static String[] getDefaultCauseMethodNames() { 275 return ArrayUtils.clone(CAUSE_METHOD_NAMES); 276 } 277 278 /** 279 * Gets a short message summarizing the exception. 280 * <p> 281 * The message returned is of the form 282 * {ClassNameWithoutPackage}: {ThrowableMessage} 283 * </p> 284 * 285 * @param th the throwable to get a message for, null returns empty string 286 * @return the message, non-null 287 * @since 2.2 288 */ 289 public static String getMessage(final Throwable th) { 290 if (th == null) { 291 return StringUtils.EMPTY; 292 } 293 final String clsName = ClassUtils.getShortClassName(th, null); 294 return clsName + ": " + StringUtils.defaultString(th.getMessage()); 295 } 296 297 /** 298 * Walks the {@link Throwable} to obtain its root cause. 299 * 300 * <p>This method walks through the exception chain until the last element, 301 * the root cause of the chain, using {@link Throwable#getCause()}, and 302 * returns that exception.</p> 303 * 304 * <p>This method handles recursive cause chains that might 305 * otherwise cause infinite loops. The cause chain is processed until 306 * the end, or until the next item in the chain is already 307 * processed. If we detect a loop, then return the element before the loop.</p> 308 309 * 310 * @param throwable the throwable to get the root cause for, may be null 311 * @return the root cause of the {@link Throwable}, 312 * {@code null} if null throwable input 313 */ 314 public static Throwable getRootCause(final Throwable throwable) { 315 final List<Throwable> list = getThrowableList(throwable); 316 return list.isEmpty() ? null : list.get(list.size() - 1); 317 } 318 319 /** 320 * Gets a short message summarizing the root cause exception. 321 * <p> 322 * The message returned is of the form 323 * {ClassNameWithoutPackage}: {ThrowableMessage} 324 * </p> 325 * 326 * @param throwable the throwable to get a message for, null returns empty string 327 * @return the message, non-null 328 * @since 2.2 329 */ 330 public static String getRootCauseMessage(final Throwable throwable) { 331 final Throwable root = getRootCause(throwable); 332 return getMessage(root == null ? throwable : root); 333 } 334 335 /** 336 * Gets a compact stack trace for the root cause of the supplied 337 * {@link Throwable}. 338 * 339 * <p>The output of this method is consistent across JDK versions. 340 * It consists of the root exception followed by each of its wrapping 341 * exceptions separated by '[wrapped]'. Note that this is the opposite 342 * order to the JDK1.4 display.</p> 343 * 344 * @param throwable the throwable to examine, may be null 345 * @return an array of stack trace frames, never null 346 * @since 2.0 347 */ 348 public static String[] getRootCauseStackTrace(final Throwable throwable) { 349 return getRootCauseStackTraceList(throwable).toArray(ArrayUtils.EMPTY_STRING_ARRAY); 350 } 351 352 /** 353 * Gets a compact stack trace for the root cause of the supplied {@link Throwable}. 354 * 355 * <p> 356 * The output of this method is consistent across JDK versions. It consists of the root exception followed by each of 357 * its wrapping exceptions separated by '[wrapped]'. Note that this is the opposite order to the JDK1.4 display. 358 * </p> 359 * 360 * @param throwable the throwable to examine, may be null 361 * @return a list of stack trace frames, never null 362 * @since 3.13.0 363 */ 364 public static List<String> getRootCauseStackTraceList(final Throwable throwable) { 365 if (throwable == null) { 366 return Collections.emptyList(); 367 } 368 final Throwable[] throwables = getThrowables(throwable); 369 final int count = throwables.length; 370 final List<String> frames = new ArrayList<>(); 371 List<String> nextTrace = getStackFrameList(throwables[count - 1]); 372 for (int i = count; --i >= 0;) { 373 final List<String> trace = nextTrace; 374 if (i != 0) { 375 nextTrace = getStackFrameList(throwables[i - 1]); 376 removeCommonFrames(trace, nextTrace); 377 } 378 if (i == count - 1) { 379 frames.add(throwables[i].toString()); 380 } else { 381 frames.add(WRAPPED_MARKER + throwables[i].toString()); 382 } 383 frames.addAll(trace); 384 } 385 return frames; 386 } 387 388 /** 389 * Gets a {@link List} of stack frames, the message 390 * is not included. Only the trace of the specified exception is 391 * returned, any caused by trace is stripped. 392 * 393 * <p>This works in most cases and will only fail if the exception 394 * message contains a line that starts with: {@code "<whitespace>at"}.</p> 395 * 396 * @param throwable is any throwable 397 * @return List of stack frames 398 */ 399 static List<String> getStackFrameList(final Throwable throwable) { 400 final String stackTrace = getStackTrace(throwable); 401 final String linebreak = System.lineSeparator(); 402 final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); 403 final List<String> list = new ArrayList<>(); 404 boolean traceStarted = false; 405 while (frames.hasMoreTokens()) { 406 final String token = frames.nextToken(); 407 // Determine if the line starts with "<whitespace>at" 408 final int at = token.indexOf("at"); 409 if (at != NOT_FOUND && token.substring(0, at).trim().isEmpty()) { 410 traceStarted = true; 411 list.add(token); 412 } else if (traceStarted) { 413 break; 414 } 415 } 416 return list; 417 } 418 419 /** 420 * Gets an array where each element is a line from the argument. 421 * 422 * <p>The end of line is determined by the value of {@link System#lineSeparator()}.</p> 423 * 424 * @param stackTrace a stack trace String 425 * @return an array where each element is a line from the argument 426 */ 427 static String[] getStackFrames(final String stackTrace) { 428 return new IterableStringTokenizer(stackTrace, System.lineSeparator()).toArray(); 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> 437 * The result of this method vary by JDK version as this method 438 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. 439 * </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, including suppressed and cause exceptions. 453 * 454 * <p> 455 * The result of this method vary by JDK version as this method 456 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. 457 * </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>This method handles recursive cause chains 481 * that might otherwise cause infinite loops. The cause chain is 482 * processed until the end, or until the next item in the 483 * chain is already in the result.</p> 484 * 485 * @param throwable the throwable to inspect, may be null 486 * @return the count of throwables, zero on 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 chains that might 503 * otherwise cause infinite loops. The cause chain is processed until 504 * the end, or until the next item in the chain is already 505 * in the result list.</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>This method handles recursive cause chains 531 * that might otherwise cause infinite loops. The cause chain is 532 * processed until the end, or until the next item in the 533 * chain is already in the result array.</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 * <p> 714 * 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. 718 * </p> 719 * <p> 720 * The output of this method is consistent across JDK versions. 721 * </p> 722 * <p> 723 * The method is equivalent to {@code printStackTrace} for throwables 724 * that don't have nested causes. 725 * </p> 726 * 727 * @param throwable the throwable to output 728 * @since 2.0 729 */ 730 public static void printRootCauseStackTrace(final Throwable throwable) { 731 printRootCauseStackTrace(throwable, System.err); 732 } 733 734 /** 735 * Prints a compact stack trace for the root cause of a throwable. 736 * 737 * <p>The compact stack trace starts with the root cause and prints 738 * stack frames up to the place where it was caught and wrapped. 739 * Then it prints the wrapped exception and continues with stack frames 740 * until the wrapper exception is caught and wrapped again, etc.</p> 741 * 742 * <p>The output of this method is consistent across JDK versions. 743 * Note that this is the opposite order to the JDK1.4 display.</p> 744 * 745 * <p>The method is equivalent to {@code printStackTrace} for throwables 746 * that don't have nested causes.</p> 747 * 748 * @param throwable the throwable to output, may be null 749 * @param printStream the stream to output to, may not be null 750 * @throws NullPointerException if the printStream is {@code null} 751 * @since 2.0 752 */ 753 @SuppressWarnings("resource") 754 public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) { 755 if (throwable == null) { 756 return; 757 } 758 Objects.requireNonNull(printStream, "printStream"); 759 getRootCauseStackTraceList(throwable).forEach(printStream::println); 760 printStream.flush(); 761 } 762 763 /** 764 * Prints a compact stack trace for the root cause of a throwable. 765 * 766 * <p>The compact stack trace starts with the root cause and prints 767 * stack frames up to the place where it was caught and wrapped. 768 * Then it prints the wrapped exception and continues with stack frames 769 * until the wrapper exception is caught and wrapped again, etc.</p> 770 * 771 * <p>The output of this method is consistent across JDK versions. 772 * Note that this is the opposite order to the JDK1.4 display.</p> 773 * 774 * <p>The method is equivalent to {@code printStackTrace} for throwables 775 * that don't have nested causes.</p> 776 * 777 * @param throwable the throwable to output, may be null 778 * @param printWriter the writer to output to, may not be null 779 * @throws NullPointerException if the printWriter is {@code null} 780 * @since 2.0 781 */ 782 @SuppressWarnings("resource") 783 public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) { 784 if (throwable == null) { 785 return; 786 } 787 Objects.requireNonNull(printWriter, "printWriter"); 788 getRootCauseStackTraceList(throwable).forEach(printWriter::println); 789 printWriter.flush(); 790 } 791 792 /** 793 * Removes common frames from the cause trace given the two stack traces. 794 * 795 * @param causeFrames stack trace of a cause throwable 796 * @param wrapperFrames stack trace of a wrapper throwable 797 * @throws NullPointerException if either argument is null 798 * @since 2.0 799 */ 800 public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) { 801 Objects.requireNonNull(causeFrames, "causeFrames"); 802 Objects.requireNonNull(wrapperFrames, "wrapperFrames"); 803 int causeFrameIndex = causeFrames.size() - 1; 804 int wrapperFrameIndex = wrapperFrames.size() - 1; 805 while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { 806 // Remove the frame from the cause trace if it is the same 807 // as in the wrapper trace 808 final String causeFrame = causeFrames.get(causeFrameIndex); 809 final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); 810 if (causeFrame.equals(wrapperFrame)) { 811 causeFrames.remove(causeFrameIndex); 812 } 813 causeFrameIndex--; 814 wrapperFrameIndex--; 815 } 816 } 817 818 /** 819 * Throws the given (usually checked) exception without adding the exception to the throws 820 * clause of the calling method. This method prevents throws clause 821 * inflation and reduces the clutter of "Caused by" exceptions in the 822 * stack trace. 823 * <p> 824 * The use of this technique may be controversial, but useful. 825 * </p> 826 * <pre> 827 * // There is no throws clause in the method signature. 828 * public int propagateExample() { 829 * try { 830 * // throws SomeCheckedException. 831 * return invocation(); 832 * } catch (SomeCheckedException e) { 833 * // Propagates a checked exception and compiles to return an int. 834 * return ExceptionUtils.rethrow(e); 835 * } 836 * } 837 * </pre> 838 * <p> 839 * This is an alternative to the more conservative approach of wrapping the 840 * checked exception in a RuntimeException: 841 * </p> 842 * <pre> 843 * // There is no throws clause in the method signature. 844 * public int wrapExample() { 845 * try { 846 * // throws IOException. 847 * return invocation(); 848 * } catch (Error e) { 849 * throw e; 850 * } catch (RuntimeException e) { 851 * // Throws an unchecked exception. 852 * throw e; 853 * } catch (Exception e) { 854 * // wraps a checked exception. 855 * throw new UndeclaredThrowableException(e); 856 * } 857 * } 858 * </pre> 859 * <p> 860 * One downside to using this approach is that the Java compiler will not 861 * allow invoking code to specify a checked exception in a catch clause 862 * unless there is some code path within the try block that has invoked a 863 * method declared with that checked exception. If the invoking site wishes 864 * to catch the shaded checked exception, it must either invoke the shaded 865 * code through a method re-declaring the desired checked exception, or 866 * catch Exception and use the {@code instanceof} operator. Either of these 867 * techniques are required when interacting with non-Java JVM code such as 868 * Jython, Scala, or Groovy, since these languages do not consider any 869 * exceptions as checked. 870 * </p> 871 * 872 * @param throwable 873 * The throwable to rethrow. 874 * @param <T> The type of the return value. 875 * @return Never actually returns, this generic type matches any type 876 * which the calling site requires. "Returning" the results of this 877 * method, as done in the propagateExample above, will satisfy the 878 * Java compiler requirement that all code paths return a value. 879 * @since 3.5 880 * @see #wrapAndThrow(Throwable) 881 */ 882 public static <T> T rethrow(final Throwable throwable) { 883 // claim that the typeErasure invocation throws a RuntimeException 884 return ExceptionUtils.<T, RuntimeException>eraseType(throwable); 885 } 886 887 /** 888 * Streams causes of a Throwable. 889 * <p> 890 * A throwable without cause will return a stream containing one element - the input throwable. A throwable with one cause 891 * will return a stream containing two elements. - the input throwable and the cause throwable. A {@code null} throwable 892 * will return a stream of count zero. 893 * </p> 894 * 895 * <p> 896 * This method handles recursive cause chains that might otherwise cause infinite loops. The cause chain is 897 * processed until the end, or until the next item in the chain is already in the result. 898 * </p> 899 * 900 * @param throwable The Throwable to traverse 901 * @return A new Stream of Throwable causes. 902 * @since 3.13.0 903 */ 904 public static Stream<Throwable> stream(final Throwable throwable) { 905 // No point building a custom Iterable as it would keep track of visited elements to avoid infinite loops 906 return getThrowableList(throwable).stream(); 907 } 908 909 /** 910 * Worker method for the {@code throwableOfType} methods. 911 * 912 * @param <T> the type of Throwable you are searching. 913 * @param throwable the throwable to inspect, may be null 914 * @param type the type to search, subclasses match, null returns null 915 * @param fromIndex the (zero-based) index of the starting position, 916 * negative treated as zero, larger than chain size returns null 917 * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares 918 * using references 919 * @return throwable of the {@code type} within throwables nested within the specified {@code throwable} 920 */ 921 private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) { 922 if (throwable == null || type == null) { 923 return null; 924 } 925 if (fromIndex < 0) { 926 fromIndex = 0; 927 } 928 final Throwable[] throwables = getThrowables(throwable); 929 if (fromIndex >= throwables.length) { 930 return null; 931 } 932 if (subclass) { 933 for (int i = fromIndex; i < throwables.length; i++) { 934 if (type.isAssignableFrom(throwables[i].getClass())) { 935 return type.cast(throwables[i]); 936 } 937 } 938 } else { 939 for (int i = fromIndex; i < throwables.length; i++) { 940 if (type.equals(throwables[i].getClass())) { 941 return type.cast(throwables[i]); 942 } 943 } 944 } 945 return null; 946 } 947 948 /** 949 * Returns the first {@link Throwable} 950 * that matches the specified class (exactly) in the exception chain. 951 * Subclasses of the specified class do not match - see 952 * {@link #throwableOfType(Throwable, Class)} for the opposite. 953 * 954 * <p>A {@code null} throwable returns {@code null}. 955 * A {@code null} type returns {@code null}. 956 * No match in the chain returns {@code null}.</p> 957 * 958 * @param <T> the type of Throwable you are searching. 959 * @param throwable the throwable to inspect, may be null 960 * @param clazz the class to search for, subclasses do not match, null returns null 961 * @return the first matching throwable from the throwable chain, null if no match or null input 962 * @since 3.10 963 */ 964 public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) { 965 return throwableOf(throwable, clazz, 0, false); 966 } 967 968 /** 969 * Returns the first {@link Throwable} 970 * that matches the specified type in the exception chain from 971 * a specified index. 972 * Subclasses of the specified class do not match - see 973 * {@link #throwableOfType(Throwable, Class, int)} for the opposite. 974 * 975 * <p>A {@code null} throwable returns {@code null}. 976 * A {@code null} type returns {@code null}. 977 * No match in the chain returns {@code null}. 978 * A negative start index is treated as zero. 979 * A start index greater than the number of throwables returns {@code null}.</p> 980 * 981 * @param <T> the type of Throwable you are searching. 982 * @param throwable the throwable to inspect, may be null 983 * @param clazz the class to search for, subclasses do not match, null returns null 984 * @param fromIndex the (zero-based) index of the starting position, 985 * negative treated as zero, larger than chain size returns null 986 * @return the first matching throwable from the throwable chain, null if no match or null input 987 * @since 3.10 988 */ 989 public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) { 990 return throwableOf(throwable, clazz, fromIndex, false); 991 } 992 993 /** 994 * Returns the throwable of the first {@link Throwable} 995 * that matches the specified class or subclass in the exception chain. 996 * Subclasses of the specified class do match - see 997 * {@link #throwableOfThrowable(Throwable, Class)} for the opposite. 998 * 999 * <p>A {@code null} throwable returns {@code null}. 1000 * A {@code null} type returns {@code null}. 1001 * No match in the chain returns {@code null}.</p> 1002 * 1003 * @param <T> the type of Throwable you are searching. 1004 * @param throwable the throwable to inspect, may be null 1005 * @param type the type to search for, subclasses match, null returns null 1006 * @return the first matching throwable from the throwable chain, null if no match or null input 1007 * @since 3.10 1008 */ 1009 public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) { 1010 return throwableOf(throwable, type, 0, true); 1011 } 1012 1013 /** 1014 * Returns the first {@link Throwable} 1015 * that matches the specified type in the exception chain from 1016 * a specified index. 1017 * Subclasses of the specified class do match - see 1018 * {@link #throwableOfThrowable(Throwable, Class)} for the opposite. 1019 * 1020 * <p>A {@code null} throwable returns {@code null}. 1021 * A {@code null} type returns {@code null}. 1022 * No match in the chain returns {@code null}. 1023 * A negative start index is treated as zero. 1024 * A start index greater than the number of throwables returns {@code null}.</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 * @param fromIndex the (zero-based) index of the starting position, 1030 * negative treated as zero, larger than chain size returns null 1031 * @return the first matching throwable from the throwable chain, null if no match or null input 1032 * @since 3.10 1033 */ 1034 public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) { 1035 return throwableOf(throwable, type, fromIndex, true); 1036 } 1037 1038 /** 1039 * Tests whether the specified {@link Throwable} is unchecked and throws it if so. 1040 * 1041 * @param <T> The Throwable type. 1042 * @param throwable the throwable to test and throw or return. 1043 * @return the given throwable. 1044 * @since 3.13.0 1045 * @deprecated Use {@link #throwUnchecked(Throwable)}. 1046 */ 1047 @Deprecated 1048 public static <T> T throwUnchecked(final T throwable) { 1049 if (throwable instanceof RuntimeException) { 1050 throw (RuntimeException) throwable; 1051 } 1052 if (throwable instanceof Error) { 1053 throw (Error) throwable; 1054 } 1055 return throwable; 1056 } 1057 1058 /** 1059 * Tests whether the specified {@link Throwable} is unchecked and throws it if so. 1060 * 1061 * @param <T> The Throwable type. 1062 * @param throwable the throwable to test and throw or return. 1063 * @return the given throwable. 1064 * @since 3.14.0 1065 */ 1066 public static <T extends Throwable> T throwUnchecked(final T throwable) { 1067 if (isUnchecked(throwable)) { 1068 throw asRuntimeException(throwable); 1069 } 1070 return throwable; 1071 } 1072 1073 /** 1074 * Throws a checked exception without adding the exception to the throws 1075 * clause of the calling method. For checked exceptions, this method throws 1076 * an UndeclaredThrowableException wrapping the checked exception. For 1077 * Errors and RuntimeExceptions, the original exception is rethrown. 1078 * <p> 1079 * The downside to using this approach is that invoking code which needs to 1080 * handle specific checked exceptions must sniff up the exception chain to 1081 * determine if the caught exception was caused by the checked exception. 1082 * </p> 1083 * 1084 * @param throwable 1085 * The throwable to rethrow. 1086 * @param <R> The type of the returned value. 1087 * @return Never actually returned, this generic type matches any type 1088 * which the calling site requires. "Returning" the results of this 1089 * method will satisfy the Java compiler requirement that all code 1090 * paths return a value. 1091 * @since 3.5 1092 * @see #asRuntimeException(Throwable) 1093 * @see #hasCause(Throwable, Class) 1094 */ 1095 public static <R> R wrapAndThrow(final Throwable throwable) { 1096 throw new UndeclaredThrowableException(throwUnchecked(throwable)); 1097 } 1098 1099 /** 1100 * Public constructor allows an instance of {@link ExceptionUtils} to be created, although that is not 1101 * normally necessary. 1102 * 1103 * @deprecated TODO Make private in 4.0. 1104 */ 1105 @Deprecated 1106 public ExceptionUtils() { 1107 // empty 1108 } 1109}