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