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