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.Objects; 028import java.util.StringTokenizer; 029 030import org.apache.commons.lang3.ArrayUtils; 031import org.apache.commons.lang3.ClassUtils; 032import org.apache.commons.lang3.StringUtils; 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 printStream the stream to output to, may not be null 628 * @throws NullPointerException if the printStream is {@code null} 629 * @since 2.0 630 */ 631 @SuppressWarnings("resource") 632 public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream printStream) { 633 if (throwable == null) { 634 return; 635 } 636 Objects.requireNonNull(printStream, "printStream"); 637 final String[] trace = getRootCauseStackTrace(throwable); 638 for (final String element : trace) { 639 printStream.println(element); 640 } 641 printStream.flush(); 642 } 643 644 /** 645 * <p>Prints a compact stack trace for the root cause of a throwable.</p> 646 * 647 * <p>The compact stack trace starts with the root cause and prints 648 * stack frames up to the place where it was caught and wrapped. 649 * Then it prints the wrapped exception and continues with stack frames 650 * until the wrapper exception is caught and wrapped again, etc.</p> 651 * 652 * <p>The output of this method is consistent across JDK versions. 653 * Note that this is the opposite order to the JDK1.4 display.</p> 654 * 655 * <p>The method is equivalent to {@code printStackTrace} for throwables 656 * that don't have nested causes.</p> 657 * 658 * @param throwable the throwable to output, may be null 659 * @param printWriter the writer to output to, may not be null 660 * @throws NullPointerException if the printWriter is {@code null} 661 * @since 2.0 662 */ 663 @SuppressWarnings("resource") 664 public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter printWriter) { 665 if (throwable == null) { 666 return; 667 } 668 Objects.requireNonNull(printWriter, "printWriter"); 669 final String[] trace = getRootCauseStackTrace(throwable); 670 for (final String element : trace) { 671 printWriter.println(element); 672 } 673 printWriter.flush(); 674 } 675 676 /** 677 * <p>Removes common frames from the cause trace given the two stack traces.</p> 678 * 679 * @param causeFrames stack trace of a cause throwable 680 * @param wrapperFrames stack trace of a wrapper throwable 681 * @throws IllegalArgumentException if either argument is null 682 * @since 2.0 683 */ 684 public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) { 685 if (causeFrames == null || wrapperFrames == null) { 686 throw new IllegalArgumentException("The List must not be null"); 687 } 688 int causeFrameIndex = causeFrames.size() - 1; 689 int wrapperFrameIndex = wrapperFrames.size() - 1; 690 while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { 691 // Remove the frame from the cause trace if it is the same 692 // as in the wrapper trace 693 final String causeFrame = causeFrames.get(causeFrameIndex); 694 final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); 695 if (causeFrame.equals(wrapperFrame)) { 696 causeFrames.remove(causeFrameIndex); 697 } 698 causeFrameIndex--; 699 wrapperFrameIndex--; 700 } 701 } 702 703 /** 704 * Throw a checked exception without adding the exception to the throws 705 * clause of the calling method. This method prevents throws clause 706 * pollution and reduces the clutter of "Caused by" exceptions in the 707 * stacktrace. 708 * <p> 709 * The use of this technique may be controversial, but exceedingly useful to 710 * library developers. 711 * <code> 712 * public int propagateExample { // note that there is no throws clause 713 * try { 714 * return invocation(); // throws IOException 715 * } catch (Exception e) { 716 * return ExceptionUtils.rethrow(e); // propagates a checked exception 717 * } 718 * } 719 * </code> 720 * <p> 721 * This is an alternative to the more conservative approach of wrapping the 722 * checked exception in a RuntimeException: 723 * <code> 724 * public int wrapExample { // note that there is no throws clause 725 * try { 726 * return invocation(); // throws IOException 727 * } catch (Error e) { 728 * throw e; 729 * } catch (RuntimeException e) { 730 * throw e; // wraps a checked exception 731 * } catch (Exception e) { 732 * throw new UndeclaredThrowableException(e); // wraps a checked exception 733 * } 734 * } 735 * </code> 736 * <p> 737 * One downside to using this approach is that the java compiler will not 738 * allow invoking code to specify a checked exception in a catch clause 739 * unless there is some code path within the try block that has invoked a 740 * method declared with that checked exception. If the invoking site wishes 741 * to catch the shaded checked exception, it must either invoke the shaded 742 * code through a method re-declaring the desired checked exception, or 743 * catch Exception and use the instanceof operator. Either of these 744 * techniques are required when interacting with non-java jvm code such as 745 * Jython, Scala, or Groovy, since these languages do not consider any 746 * exceptions as checked. 747 * 748 * @param throwable 749 * The throwable to rethrow. 750 * @param <R> The type of the returned value. 751 * @return Never actually returned, this generic type matches any type 752 * which the calling site requires. "Returning" the results of this 753 * method, as done in the propagateExample above, will satisfy the 754 * java compiler requirement that all code paths return a value. 755 * @since 3.5 756 * @see #wrapAndThrow(Throwable) 757 */ 758 public static <R> R rethrow(final Throwable throwable) { 759 // claim that the typeErasure invocation throws a RuntimeException 760 return ExceptionUtils.<R, RuntimeException>typeErasure(throwable); 761 } 762 763 /** 764 * <p>Worker method for the {@code throwableOfType} methods.</p> 765 * 766 * @param <T> the type of Throwable you are searching. 767 * @param throwable the throwable to inspect, may be null 768 * @param type the type to search, subclasses match, null returns null 769 * @param fromIndex the (zero-based) index of the starting position, 770 * negative treated as zero, larger than chain size returns null 771 * @param subclass if {@code true}, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares 772 * using references 773 * @return throwable of the {@code type} within throwables nested within the specified {@code throwable} 774 */ 775 private static <T extends Throwable> T throwableOf(final Throwable throwable, final Class<T> type, int fromIndex, final boolean subclass) { 776 if (throwable == null || type == null) { 777 return null; 778 } 779 if (fromIndex < 0) { 780 fromIndex = 0; 781 } 782 final Throwable[] throwables = getThrowables(throwable); 783 if (fromIndex >= throwables.length) { 784 return null; 785 } 786 if (subclass) { 787 for (int i = fromIndex; i < throwables.length; i++) { 788 if (type.isAssignableFrom(throwables[i].getClass())) { 789 return type.cast(throwables[i]); 790 } 791 } 792 } else { 793 for (int i = fromIndex; i < throwables.length; i++) { 794 if (type.equals(throwables[i].getClass())) { 795 return type.cast(throwables[i]); 796 } 797 } 798 } 799 return null; 800 } 801 802 /** 803 * <p>Returns the first {@code Throwable} 804 * that matches the specified class (exactly) in the exception chain. 805 * Subclasses of the specified class do not match - see 806 * {@link #throwableOfType(Throwable, Class)} for the opposite.</p> 807 * 808 * <p>A {@code null} throwable returns {@code null}. 809 * A {@code null} type returns {@code null}. 810 * No match in the chain returns {@code null}.</p> 811 * 812 * @param <T> the type of Throwable you are searching. 813 * @param throwable the throwable to inspect, may be null 814 * @param clazz the class to search for, subclasses do not match, null returns null 815 * @return the first matching throwable from the throwable chain, null if no match or null input 816 * @since 3.10 817 */ 818 public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz) { 819 return throwableOf(throwable, clazz, 0, false); 820 } 821 822 /** 823 * <p>Returns the first {@code Throwable} 824 * that matches the specified type in the exception chain from 825 * a specified index. 826 * Subclasses of the specified class do not match - see 827 * {@link #throwableOfType(Throwable, Class, int)} for the opposite.</p> 828 * 829 * <p>A {@code null} throwable returns {@code null}. 830 * A {@code null} type returns {@code null}. 831 * No match in the chain returns {@code null}. 832 * A negative start index is treated as zero. 833 * A start index greater than the number of throwables returns {@code null}.</p> 834 * 835 * @param <T> the type of Throwable you are searching. 836 * @param throwable the throwable to inspect, may be null 837 * @param clazz the class to search for, subclasses do not match, null returns null 838 * @param fromIndex the (zero-based) index of the starting position, 839 * negative treated as zero, larger than chain size returns null 840 * @return the first matching throwable from the throwable chain, null if no match or null input 841 * @since 3.10 842 */ 843 public static <T extends Throwable> T throwableOfThrowable(final Throwable throwable, final Class<T> clazz, final int fromIndex) { 844 return throwableOf(throwable, clazz, fromIndex, false); 845 } 846 847 /** 848 * <p>Returns the throwable of the first {@code Throwable} 849 * that matches the specified class or subclass in the exception chain. 850 * Subclasses of the specified class do match - see 851 * {@link #throwableOfThrowable(Throwable, Class)} for the opposite..</p> 852 * 853 * <p>A {@code null} throwable returns {@code null}. 854 * A {@code null} type returns {@code null}. 855 * No match in the chain returns {@code null}.</p> 856 * 857 * @param <T> the type of Throwable you are searching. 858 * @param throwable the throwable to inspect, may be null 859 * @param type the type to search for, subclasses match, null returns null 860 * @return the first matching throwable from the throwable chain, null if no match or null input 861 * @since 3.10 862 */ 863 public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type) { 864 return throwableOf(throwable, type, 0, true); 865 } 866 867 /** 868 * <p>Returns the first {@code Throwable} 869 * that matches the specified type in the exception chain from 870 * a specified index. 871 * Subclasses of the specified class do match - see 872 * {@link #throwableOfThrowable(Throwable, Class)} for the opposite.</p> 873 * 874 * <p>A {@code null} throwable returns {@code null}. 875 * A {@code null} type returns {@code null}. 876 * No match in the chain returns {@code null}. 877 * A negative start index is treated as zero. 878 * A start index greater than the number of throwables returns {@code null}.</p> 879 * 880 * @param <T> the type of Throwable you are searching. 881 * @param throwable the throwable to inspect, may be null 882 * @param type the type to search for, subclasses match, null returns null 883 * @param fromIndex the (zero-based) index of the starting position, 884 * negative treated as zero, larger than chain size returns null 885 * @return the first matching throwable from the throwable chain, null if no match or null input 886 * @since 3.10 887 */ 888 public static <T extends Throwable> T throwableOfType(final Throwable throwable, final Class<T> type, final int fromIndex) { 889 return throwableOf(throwable, type, fromIndex, true); 890 } 891 892 /** 893 * Claim a Throwable is another Exception type using type erasure. This 894 * hides a checked exception from the java compiler, allowing a checked 895 * exception to be thrown without having the exception in the method's throw 896 * clause. 897 */ 898 @SuppressWarnings("unchecked") 899 private static <R, T extends Throwable> R typeErasure(final Throwable throwable) throws T { 900 throw (T) throwable; 901 } 902 903 /** 904 * Throw a checked exception without adding the exception to the throws 905 * clause of the calling method. For checked exceptions, this method throws 906 * an UndeclaredThrowableException wrapping the checked exception. For 907 * Errors and RuntimeExceptions, the original exception is rethrown. 908 * <p> 909 * The downside to using this approach is that invoking code which needs to 910 * handle specific checked exceptions must sniff up the exception chain to 911 * determine if the caught exception was caused by the checked exception. 912 * 913 * @param throwable 914 * The throwable to rethrow. 915 * @param <R> The type of the returned value. 916 * @return Never actually returned, this generic type matches any type 917 * which the calling site requires. "Returning" the results of this 918 * method will satisfy the java compiler requirement that all code 919 * paths return a value. 920 * @since 3.5 921 * @see #rethrow(Throwable) 922 * @see #hasCause(Throwable, Class) 923 */ 924 public static <R> R wrapAndThrow(final Throwable throwable) { 925 if (throwable instanceof RuntimeException) { 926 throw (RuntimeException) throwable; 927 } 928 if (throwable instanceof Error) { 929 throw (Error) throwable; 930 } 931 throw new UndeclaredThrowableException(throwable); 932 } 933 934 /** 935 * <p> 936 * Public constructor allows an instance of {@code ExceptionUtils} to be created, although that is not 937 * normally necessary. 938 * </p> 939 */ 940 public ExceptionUtils() { 941 super(); 942 } 943}