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