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 #getCause(Throwable)}, 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 none found or null throwable input 183 */ 184 public static Throwable getRootCause(final Throwable throwable) { 185 final List<Throwable> list = getThrowableList(throwable); 186 return list.size() < 2 ? 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 Commons Lang 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) == false) { 282 list.add(throwable); 283 throwable = ExceptionUtils.getCause(throwable); 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 = ExceptionUtils.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 for (int j = 0; j < trace.size(); j++) { 531 frames.add(trace.get(j)); 532 } 533 } 534 return frames.toArray(new String[frames.size()]); 535 } 536 537 /** 538 * <p>Removes common frames from the cause trace given the two stack traces.</p> 539 * 540 * @param causeFrames stack trace of a cause throwable 541 * @param wrapperFrames stack trace of a wrapper throwable 542 * @throws IllegalArgumentException if either argument is null 543 * @since 2.0 544 */ 545 public static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) { 546 if (causeFrames == null || wrapperFrames == null) { 547 throw new IllegalArgumentException("The List must not be null"); 548 } 549 int causeFrameIndex = causeFrames.size() - 1; 550 int wrapperFrameIndex = wrapperFrames.size() - 1; 551 while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { 552 // Remove the frame from the cause trace if it is the same 553 // as in the wrapper trace 554 final String causeFrame = causeFrames.get(causeFrameIndex); 555 final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); 556 if (causeFrame.equals(wrapperFrame)) { 557 causeFrames.remove(causeFrameIndex); 558 } 559 causeFrameIndex--; 560 wrapperFrameIndex--; 561 } 562 } 563 564 //----------------------------------------------------------------------- 565 /** 566 * <p>Gets the stack trace from a Throwable as a String.</p> 567 * 568 * <p>The result of this method vary by JDK version as this method 569 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. 570 * On JDK1.3 and earlier, the cause exception will not be shown 571 * unless the specified throwable alters printStackTrace.</p> 572 * 573 * @param throwable the <code>Throwable</code> to be examined 574 * @return the stack trace as generated by the exception's 575 * <code>printStackTrace(PrintWriter)</code> method 576 */ 577 public static String getStackTrace(final Throwable throwable) { 578 final StringWriter sw = new StringWriter(); 579 final PrintWriter pw = new PrintWriter(sw, true); 580 throwable.printStackTrace(pw); 581 return sw.getBuffer().toString(); 582 } 583 584 /** 585 * <p>Captures the stack trace associated with the specified 586 * <code>Throwable</code> object, decomposing it into a list of 587 * stack frames.</p> 588 * 589 * <p>The result of this method vary by JDK version as this method 590 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. 591 * On JDK1.3 and earlier, the cause exception will not be shown 592 * unless the specified throwable alters printStackTrace.</p> 593 * 594 * @param throwable the <code>Throwable</code> to examine, may be null 595 * @return an array of strings describing each stack frame, never null 596 */ 597 public static String[] getStackFrames(final Throwable throwable) { 598 if (throwable == null) { 599 return ArrayUtils.EMPTY_STRING_ARRAY; 600 } 601 return getStackFrames(getStackTrace(throwable)); 602 } 603 604 //----------------------------------------------------------------------- 605 /** 606 * <p>Returns an array where each element is a line from the argument.</p> 607 * 608 * <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p> 609 * 610 * @param stackTrace a stack trace String 611 * @return an array where each element is a line from the argument 612 */ 613 static String[] getStackFrames(final String stackTrace) { 614 final String linebreak = System.lineSeparator(); 615 final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); 616 final List<String> list = new ArrayList<>(); 617 while (frames.hasMoreTokens()) { 618 list.add(frames.nextToken()); 619 } 620 return list.toArray(new String[list.size()]); 621 } 622 623 /** 624 * <p>Produces a <code>List</code> of stack frames - the message 625 * is not included. Only the trace of the specified exception is 626 * returned, any caused by trace is stripped.</p> 627 * 628 * <p>This works in most cases - it will only fail if the exception 629 * message contains a line that starts with: 630 * <code>" at".</code></p> 631 * 632 * @param t is any throwable 633 * @return List of stack frames 634 */ 635 static List<String> getStackFrameList(final Throwable t) { 636 final String stackTrace = getStackTrace(t); 637 final String linebreak = System.lineSeparator(); 638 final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); 639 final List<String> list = new ArrayList<>(); 640 boolean traceStarted = false; 641 while (frames.hasMoreTokens()) { 642 final String token = frames.nextToken(); 643 // Determine if the line starts with <whitespace>at 644 final int at = token.indexOf("at"); 645 if (at != -1 && token.substring(0, at).trim().isEmpty()) { 646 traceStarted = true; 647 list.add(token); 648 } else if (traceStarted) { 649 break; 650 } 651 } 652 return list; 653 } 654 655 //----------------------------------------------------------------------- 656 /** 657 * Gets a short message summarising the exception. 658 * <p> 659 * The message returned is of the form 660 * {ClassNameWithoutPackage}: {ThrowableMessage} 661 * 662 * @param th the throwable to get a message for, null returns empty string 663 * @return the message, non-null 664 * @since Commons Lang 2.2 665 */ 666 public static String getMessage(final Throwable th) { 667 if (th == null) { 668 return StringUtils.EMPTY; 669 } 670 final String clsName = ClassUtils.getShortClassName(th, null); 671 final String msg = th.getMessage(); 672 return clsName + ": " + StringUtils.defaultString(msg); 673 } 674 675 //----------------------------------------------------------------------- 676 /** 677 * Gets a short message summarising the root cause exception. 678 * <p> 679 * The message returned is of the form 680 * {ClassNameWithoutPackage}: {ThrowableMessage} 681 * 682 * @param th the throwable to get a message for, null returns empty string 683 * @return the message, non-null 684 * @since Commons Lang 2.2 685 */ 686 public static String getRootCauseMessage(final Throwable th) { 687 Throwable root = ExceptionUtils.getRootCause(th); 688 root = root == null ? th : root; 689 return getMessage(root); 690 } 691 692 /** 693 * Throw a checked exception without adding the exception to the throws 694 * clause of the calling method. This method prevents throws clause 695 * pollution and reduces the clutter of "Caused by" exceptions in the 696 * stacktrace. 697 * <p> 698 * The use of this technique may be controversial, but exceedingly useful to 699 * library developers. 700 * <code> 701 * public int propagateExample { // note that there is no throws clause 702 * try { 703 * return invocation(); // throws IOException 704 * } catch (Exception e) { 705 * return ExceptionUtils.rethrow(e); // propagates a checked exception 706 * } 707 * } 708 * </code> 709 * <p> 710 * This is an alternative to the more conservative approach of wrapping the 711 * checked exception in a RuntimeException: 712 * <code> 713 * public int wrapExample { // note that there is no throws clause 714 * try { 715 * return invocation(); // throws IOException 716 * } catch (Error e) { 717 * throw e; 718 * } catch (RuntimeException e) { 719 * throw e; // wraps a checked exception 720 * } catch (Exception e) { 721 * throw new UndeclaredThrowableException(e); // wraps a checked exception 722 * } 723 * } 724 * </code> 725 * <p> 726 * One downside to using this approach is that the java compiler will not 727 * allow invoking code to specify a checked exception in a catch clause 728 * unless there is some code path within the try block that has invoked a 729 * method declared with that checked exception. If the invoking site wishes 730 * to catch the shaded checked exception, it must either invoke the shaded 731 * code through a method re-declaring the desired checked exception, or 732 * catch Exception and use the instanceof operator. Either of these 733 * techniques are required when interacting with non-java jvm code such as 734 * Jython, Scala, or Groovy, since these languages do not consider any 735 * exceptions as checked. 736 * 737 * @param throwable 738 * The throwable to rethrow. 739 * @param <R> The type of the returned value. 740 * @return Never actually returned, this generic type matches any type 741 * which the calling site requires. "Returning" the results of this 742 * method, as done in the propagateExample above, will satisfy the 743 * java compiler requirement that all code paths return a value. 744 * @since 3.5 745 * @see #wrapAndThrow(Throwable) 746 */ 747 public static <R> R rethrow(final Throwable throwable) { 748 // claim that the typeErasure invocation throws a RuntimeException 749 return ExceptionUtils.<R, RuntimeException> typeErasure(throwable); 750 } 751 752 /** 753 * Claim a Throwable is another Exception type using type erasure. This 754 * hides a checked exception from the java compiler, allowing a checked 755 * exception to be thrown without having the exception in the method's throw 756 * clause. 757 */ 758 @SuppressWarnings("unchecked") 759 private static <R, T extends Throwable> R typeErasure(final Throwable throwable) throws T { 760 throw (T) throwable; 761 } 762 763 /** 764 * Throw a checked exception without adding the exception to the throws 765 * clause of the calling method. For checked exceptions, this method throws 766 * an UndeclaredThrowableException wrapping the checked exception. For 767 * Errors and RuntimeExceptions, the original exception is rethrown. 768 * <p> 769 * The downside to using this approach is that invoking code which needs to 770 * handle specific checked exceptions must sniff up the exception chain to 771 * determine if the caught exception was caused by the checked exception. 772 * 773 * @param throwable 774 * The throwable to rethrow. 775 * @param <R> The type of the returned value. 776 * @return Never actually returned, this generic type matches any type 777 * which the calling site requires. "Returning" the results of this 778 * method will satisfy the java compiler requirement that all code 779 * paths return a value. 780 * @since 3.5 781 * @see #rethrow(Throwable) 782 * @see #hasCause(Throwable, Class) 783 */ 784 public static <R> R wrapAndThrow(final Throwable throwable) { 785 if (throwable instanceof RuntimeException) { 786 throw (RuntimeException) throwable; 787 } 788 if (throwable instanceof Error) { 789 throw (Error) throwable; 790 } 791 throw new UndeclaredThrowableException(throwable); 792 } 793 794 /** 795 * Does the throwable's causal chain have an immediate or wrapped exception 796 * of the given type? 797 * 798 * @param chain 799 * The root of a Throwable causal chain. 800 * @param type 801 * The exception type to test. 802 * @return true, if chain is an instance of type or is an 803 * UndeclaredThrowableException wrapping a cause. 804 * @since 3.5 805 * @see #wrapAndThrow(Throwable) 806 */ 807 public static boolean hasCause(Throwable chain, 808 final Class<? extends Throwable> type) { 809 if (chain instanceof UndeclaredThrowableException) { 810 chain = chain.getCause(); 811 } 812 return type.isInstance(chain); 813 } 814}