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