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 co.aikar.commands.apachecommonslang; 018 019import java.io.PrintStream; 020import java.io.PrintWriter; 021import java.io.StringWriter; 022import java.lang.reflect.Field; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.sql.SQLException; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.List; 029import java.util.StringTokenizer; 030 031/** 032 * <p>Provides utilities for manipulating and examining 033 * <code>Throwable</code> objects.</p> 034 * 035 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 036 * @author Dmitri Plotnikov 037 * @author Stephen Colebourne 038 * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a> 039 * @author Pete Gieser 040 * @since 1.0 041 * @version $Id$ 042 */ 043public class ApacheCommonsExceptionUtil { 044 private static final String LINE_SEPARATOR = System.getProperty("line.separator"); 045 046 /** 047 * <p>Used when printing stack frames to denote the start of a 048 * wrapped exception.</p> 049 * 050 * <p>Package private for accessibility by test suite.</p> 051 */ 052 static final String WRAPPED_MARKER = " [wrapped] "; 053 054 /** 055 * <p>The names of methods commonly used to access a wrapped exception.</p> 056 */ 057 private static String[] CAUSE_METHOD_NAMES = { 058 "getCause", 059 "getNextException", 060 "getTargetException", 061 "getException", 062 "getSourceException", 063 "getRootCause", 064 "getCausedByException", 065 "getNested", 066 "getLinkedException", 067 "getNestedException", 068 "getLinkedCause", 069 "getThrowable", 070 }; 071 072 /** 073 * <p>The Method object for Java 1.4 getCause.</p> 074 */ 075 private static final Method THROWABLE_CAUSE_METHOD; 076 077 /** 078 * <p>The Method object for Java 1.4 initCause.</p> 079 */ 080 private static final Method THROWABLE_INITCAUSE_METHOD; 081 082 static { 083 Method causeMethod; 084 try { 085 causeMethod = Throwable.class.getMethod("getCause", null); 086 } catch (Exception e) { 087 causeMethod = null; 088 } 089 THROWABLE_CAUSE_METHOD = causeMethod; 090 try { 091 causeMethod = Throwable.class.getMethod("initCause", Throwable.class); 092 } catch (Exception e) { 093 causeMethod = null; 094 } 095 THROWABLE_INITCAUSE_METHOD = causeMethod; 096 } 097 098 /** 099 * <p> 100 * Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not 101 * normally necessary. 102 * </p> 103 */ 104 public ApacheCommonsExceptionUtil() { 105 super(); 106 } 107 108 //----------------------------------------------------------------------- 109 /** 110 * <p>Adds to the list of method names used in the search for <code>Throwable</code> 111 * objects.</p> 112 * 113 * @param methodName the methodName to add to the list, <code>null</code> 114 * and empty strings are ignored 115 * @since 2.0 116 */ 117 public static void addCauseMethodName(String methodName) { 118 if (methodName != null && !methodName.isEmpty() && !isCauseMethodName(methodName)) { 119 List list = getCauseMethodNameList(); 120 if (list.add(methodName)) { 121 CAUSE_METHOD_NAMES = toArray(list); 122 } 123 } 124 } 125 126 /** 127 * <p>Removes from the list of method names used in the search for <code>Throwable</code> 128 * objects.</p> 129 * 130 * @param methodName the methodName to remove from the list, <code>null</code> 131 * and empty strings are ignored 132 * @since 2.1 133 */ 134 public static void removeCauseMethodName(String methodName) { 135 if (methodName != null && !methodName.isEmpty()) { 136 List list = getCauseMethodNameList(); 137 if (list.remove(methodName)) { 138 CAUSE_METHOD_NAMES = toArray(list); 139 } 140 } 141 } 142 143 /** 144 * <p>Sets the cause of a <code>Throwable</code> using introspection, allowing 145 * source code compatibility between pre-1.4 and post-1.4 Java releases.</p> 146 * 147 * <p>The typical use of this method is inside a constructor as in 148 * the following example:</p> 149 * 150 * <pre> 151 * import org.apache.commons.lang.exception.ExceptionUtils; 152 * 153 * public class MyException extends Exception { 154 * 155 * public MyException(String msg) { 156 * super(msg); 157 * } 158 * 159 * public MyException(String msg, Throwable cause) { 160 * super(msg); 161 * ExceptionUtils.setCause(this, cause); 162 * } 163 * } 164 * </pre> 165 * 166 * @param target the target <code>Throwable</code> 167 * @param cause the <code>Throwable</code> to set in the target 168 * @return a <code>true</code> if the target has been modified 169 * @since 2.2 170 */ 171 public static boolean setCause(Throwable target, Throwable cause) { 172 if (target == null) { 173 throw new IllegalArgumentException("target"); 174 } 175 Object[] causeArgs = new Object[]{cause}; 176 boolean modifiedTarget = false; 177 if (THROWABLE_INITCAUSE_METHOD != null) { 178 try { 179 THROWABLE_INITCAUSE_METHOD.invoke(target, causeArgs); 180 modifiedTarget = true; 181 } catch (IllegalAccessException ignored) { 182 // Exception ignored. 183 } catch (InvocationTargetException ignored) { 184 // Exception ignored. 185 } 186 } 187 try { 188 Method setCauseMethod = target.getClass().getMethod("setCause", Throwable.class); 189 setCauseMethod.invoke(target, causeArgs); 190 modifiedTarget = true; 191 } catch (NoSuchMethodException ignored) { 192 // Exception ignored. 193 } catch (IllegalAccessException ignored) { 194 // Exception ignored. 195 } catch (InvocationTargetException ignored) { 196 // Exception ignored. 197 } 198 return modifiedTarget; 199 } 200 201 /** 202 * Returns the given list as a <code>String[]</code>. 203 * @param list a list to transform. 204 * @return the given list as a <code>String[]</code>. 205 */ 206 private static String[] toArray(List list) { 207 return (String[]) list.toArray(new String[list.size()]); 208 } 209 210 /** 211 * Returns {@link #CAUSE_METHOD_NAMES} as a List. 212 * 213 * @return {@link #CAUSE_METHOD_NAMES} as a List. 214 */ 215 private static ArrayList getCauseMethodNameList() { 216 return new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES)); 217 } 218 219 /** 220 * <p>Tests if the list of method names used in the search for <code>Throwable</code> 221 * objects include the given name.</p> 222 * 223 * @param methodName the methodName to search in the list. 224 * @return if the list of method names used in the search for <code>Throwable</code> 225 * objects include the given name. 226 * @since 2.1 227 */ 228 public static boolean isCauseMethodName(String methodName) { 229 return ApacheCommonsLangUtil.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0; 230 } 231 232 //----------------------------------------------------------------------- 233 /** 234 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> 235 * 236 * <p>The method searches for methods with specific names that return a 237 * <code>Throwable</code> object. This will pick up most wrapping exceptions, 238 * including those from JDK 1.4, and 239 * The method names can be added to using {@link #addCauseMethodName(String)}.</p> 240 * 241 * <p>The default list searched for are:</p> 242 * <ul> 243 * <li><code>getCause()</code></li> 244 * <li><code>getNextException()</code></li> 245 * <li><code>getTargetException()</code></li> 246 * <li><code>getException()</code></li> 247 * <li><code>getSourceException()</code></li> 248 * <li><code>getRootCause()</code></li> 249 * <li><code>getCausedByException()</code></li> 250 * <li><code>getNested()</code></li> 251 * </ul> 252 * 253 * <p>In the absence of any such method, the object is inspected for a 254 * <code>detail</code> field assignable to a <code>Throwable</code>.</p> 255 * 256 * <p>If none of the above is found, returns <code>null</code>.</p> 257 * 258 * @param throwable the throwable to introspect for a cause, may be null 259 * @return the cause of the <code>Throwable</code>, 260 * <code>null</code> if none found or null throwable input 261 * @since 1.0 262 */ 263 public static Throwable getCause(Throwable throwable) { 264 return getCause(throwable, CAUSE_METHOD_NAMES); 265 } 266 267 /** 268 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> 269 * 270 * <ol> 271 * <li>Try known exception types.</li> 272 * <li>Try the supplied array of method names.</li> 273 * <li>Try the field 'detail'.</li> 274 * </ol> 275 * 276 * <p>A <code>null</code> set of method names means use the default set. 277 * A <code>null</code> in the set of method names will be ignored.</p> 278 * 279 * @param throwable the throwable to introspect for a cause, may be null 280 * @param methodNames the method names, null treated as default set 281 * @return the cause of the <code>Throwable</code>, 282 * <code>null</code> if none found or null throwable input 283 * @since 1.0 284 */ 285 public static Throwable getCause(Throwable throwable, String[] methodNames) { 286 if (throwable == null) { 287 return null; 288 } 289 Throwable cause = getCauseUsingWellKnownTypes(throwable); 290 if (cause == null) { 291 if (methodNames == null) { 292 methodNames = CAUSE_METHOD_NAMES; 293 } 294 for (int i = 0; i < methodNames.length; i++) { 295 String methodName = methodNames[i]; 296 if (methodName != null) { 297 cause = getCauseUsingMethodName(throwable, methodName); 298 if (cause != null) { 299 break; 300 } 301 } 302 } 303 304 if (cause == null) { 305 cause = getCauseUsingFieldName(throwable, "detail"); 306 } 307 } 308 return cause; 309 } 310 311 /** 312 * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p> 313 * 314 * <p>This method walks through the exception chain to the last element, 315 * "root" of the tree, using {@link #getCause(Throwable)}, and 316 * returns that exception.</p> 317 * 318 * <p>From version 2.2, this method handles recursive cause structures 319 * that might otherwise cause infinite loops. If the throwable parameter 320 * has a cause of itself, then null will be returned. If the throwable 321 * parameter cause chain loops, the last element in the chain before the 322 * loop is returned.</p> 323 * 324 * @param throwable the throwable to get the root cause for, may be null 325 * @return the root cause of the <code>Throwable</code>, 326 * <code>null</code> if none found or null throwable input 327 */ 328 public static Throwable getRootCause(Throwable throwable) { 329 List list = getThrowableList(throwable); 330 return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1)); 331 } 332 333 /** 334 * <p>Finds a <code>Throwable</code> for known types.</p> 335 * 336 * <p>Uses <code>instanceof</code> checks to examine the exception, 337 * looking for well known types which could contain chained or 338 * wrapped exceptions.</p> 339 * 340 * @param throwable the exception to examine 341 * @return the wrapped exception, or <code>null</code> if not found 342 */ 343 private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) { 344 if (throwable instanceof Nestable) { 345 return throwable.getCause(); 346 } else if (throwable instanceof SQLException) { 347 return ((SQLException) throwable).getNextException(); 348 } else if (throwable instanceof InvocationTargetException) { 349 return ((InvocationTargetException) throwable).getTargetException(); 350 } else { 351 return null; 352 } 353 } 354 355 /** 356 * <p>Finds a <code>Throwable</code> by method name.</p> 357 * 358 * @param throwable the exception to examine 359 * @param methodName the name of the method to find and invoke 360 * @return the wrapped exception, or <code>null</code> if not found 361 */ 362 private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { 363 Method method = null; 364 try { 365 method = throwable.getClass().getMethod(methodName, null); 366 } catch (NoSuchMethodException ignored) { 367 // exception ignored 368 } catch (SecurityException ignored) { 369 // exception ignored 370 } 371 372 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { 373 try { 374 return (Throwable) method.invoke(throwable); 375 } catch (IllegalAccessException ignored) { 376 // exception ignored 377 } catch (IllegalArgumentException ignored) { 378 // exception ignored 379 } catch (InvocationTargetException ignored) { 380 // exception ignored 381 } 382 } 383 return null; 384 } 385 386 /** 387 * <p>Finds a <code>Throwable</code> by field name.</p> 388 * 389 * @param throwable the exception to examine 390 * @param fieldName the name of the attribute to examine 391 * @return the wrapped exception, or <code>null</code> if not found 392 */ 393 private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) { 394 Field field = null; 395 try { 396 field = throwable.getClass().getField(fieldName); 397 } catch (NoSuchFieldException ignored) { 398 // exception ignored 399 } catch (SecurityException ignored) { 400 // exception ignored 401 } 402 403 if (field != null && Throwable.class.isAssignableFrom(field.getType())) { 404 try { 405 return (Throwable) field.get(throwable); 406 } catch (IllegalAccessException ignored) { 407 // exception ignored 408 } catch (IllegalArgumentException ignored) { 409 // exception ignored 410 } 411 } 412 return null; 413 } 414 415 //----------------------------------------------------------------------- 416 /** 417 * <p>Checks if the Throwable class has a <code>getCause</code> method.</p> 418 * 419 * <p>This is true for JDK 1.4 and above.</p> 420 * 421 * @return true if Throwable is nestable 422 * @since 2.0 423 */ 424 public static boolean isThrowableNested() { 425 return THROWABLE_CAUSE_METHOD != null; 426 } 427 428 /** 429 * <p>Checks whether this <code>Throwable</code> class can store a cause.</p> 430 * 431 * <p>This method does <b>not</b> check whether it actually does store a cause.<p> 432 * 433 * @param throwable the <code>Throwable</code> to examine, may be null 434 * @return boolean <code>true</code> if nested otherwise <code>false</code> 435 * @since 2.0 436 */ 437 public static boolean isNestedThrowable(Throwable throwable) { 438 if (throwable == null) { 439 return false; 440 } 441 442 if (throwable instanceof Nestable) { 443 return true; 444 } else if (throwable instanceof SQLException) { 445 return true; 446 } else if (throwable instanceof InvocationTargetException) { 447 return true; 448 } else if (isThrowableNested()) { 449 return true; 450 } 451 452 Class cls = throwable.getClass(); 453 for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) { 454 try { 455 Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null); 456 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { 457 return true; 458 } 459 } catch (NoSuchMethodException ignored) { 460 // exception ignored 461 } catch (SecurityException ignored) { 462 // exception ignored 463 } 464 } 465 466 try { 467 Field field = cls.getField("detail"); 468 if (field != null) { 469 return true; 470 } 471 } catch (NoSuchFieldException ignored) { 472 // exception ignored 473 } catch (SecurityException ignored) { 474 // exception ignored 475 } 476 477 return false; 478 } 479 480 //----------------------------------------------------------------------- 481 /** 482 * <p>Counts the number of <code>Throwable</code> objects in the 483 * exception chain.</p> 484 * 485 * <p>A throwable without cause will return <code>1</code>. 486 * A throwable with one cause will return <code>2</code> and so on. 487 * A <code>null</code> throwable will return <code>0</code>.</p> 488 * 489 * <p>From version 2.2, this method handles recursive cause structures 490 * that might otherwise cause infinite loops. The cause chain is 491 * processed until the end is reached, or until the next item in the 492 * chain is already in the result set.</p> 493 * 494 * @param throwable the throwable to inspect, may be null 495 * @return the count of throwables, zero if null input 496 */ 497 public static int getThrowableCount(Throwable throwable) { 498 return getThrowableList(throwable).size(); 499 } 500 501 /** 502 * <p>Returns the list of <code>Throwable</code> objects in the 503 * exception chain.</p> 504 * 505 * <p>A throwable without cause will return an array containing 506 * one element - the input throwable. 507 * A throwable with one cause will return an array containing 508 * two elements. - the input throwable and the cause throwable. 509 * A <code>null</code> throwable will return an array of size zero.</p> 510 * 511 * <p>From version 2.2, this method handles recursive cause structures 512 * that might otherwise cause infinite loops. The cause chain is 513 * processed until the end is reached, or until the next item in the 514 * chain is already in the result set.</p> 515 * 516 * @see #getThrowableList(Throwable) 517 * @param throwable the throwable to inspect, may be null 518 * @return the array of throwables, never null 519 */ 520 public static Throwable[] getThrowables(Throwable throwable) { 521 List list = getThrowableList(throwable); 522 return (Throwable[]) list.toArray(new Throwable[list.size()]); 523 } 524 525 /** 526 * <p>Returns the list of <code>Throwable</code> objects in the 527 * exception chain.</p> 528 * 529 * <p>A throwable without cause will return a list containing 530 * one element - the input throwable. 531 * A throwable with one cause will return a list containing 532 * two elements. - the input throwable and the cause throwable. 533 * A <code>null</code> throwable will return a list of size zero.</p> 534 * 535 * <p>This method handles recursive cause structures that might 536 * otherwise cause infinite loops. The cause chain is processed until 537 * the end is reached, or until the next item in the chain is already 538 * in the result set.</p> 539 * 540 * @param throwable the throwable to inspect, may be null 541 * @return the list of throwables, never null 542 * @since Commons Lang 2.2 543 */ 544 public static List getThrowableList(Throwable throwable) { 545 List list = new ArrayList(); 546 while (throwable != null && list.contains(throwable) == false) { 547 list.add(throwable); 548 throwable = getCause(throwable); 549 } 550 return list; 551 } 552 553 //----------------------------------------------------------------------- 554 /** 555 * <p>Returns the (zero based) index of the first <code>Throwable</code> 556 * that matches the specified class (exactly) in the exception chain. 557 * Subclasses of the specified class do not match - see 558 * {@link #indexOfType(Throwable, Class)} for the opposite.</p> 559 * 560 * <p>A <code>null</code> throwable returns <code>-1</code>. 561 * A <code>null</code> type returns <code>-1</code>. 562 * No match in the chain returns <code>-1</code>.</p> 563 * 564 * @param throwable the throwable to inspect, may be null 565 * @param clazz the class to search for, subclasses do not match, null returns -1 566 * @return the index into the throwable chain, -1 if no match or null input 567 */ 568 public static int indexOfThrowable(Throwable throwable, Class clazz) { 569 return indexOf(throwable, clazz, 0, false); 570 } 571 572 /** 573 * <p>Returns the (zero based) index of the first <code>Throwable</code> 574 * that matches the specified type in the exception chain from 575 * a specified index. 576 * Subclasses of the specified class do not match - see 577 * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p> 578 * 579 * <p>A <code>null</code> throwable returns <code>-1</code>. 580 * A <code>null</code> type returns <code>-1</code>. 581 * No match in the chain returns <code>-1</code>. 582 * A negative start index is treated as zero. 583 * A start index greater than the number of throwables returns <code>-1</code>.</p> 584 * 585 * @param throwable the throwable to inspect, may be null 586 * @param clazz the class to search for, subclasses do not match, null returns -1 587 * @param fromIndex the (zero based) index of the starting position, 588 * negative treated as zero, larger than chain size returns -1 589 * @return the index into the throwable chain, -1 if no match or null input 590 */ 591 public static int indexOfThrowable(Throwable throwable, Class clazz, int fromIndex) { 592 return indexOf(throwable, clazz, fromIndex, false); 593 } 594 595 //----------------------------------------------------------------------- 596 /** 597 * <p>Returns the (zero based) index of the first <code>Throwable</code> 598 * that matches the specified class or subclass in the exception chain. 599 * Subclasses of the specified class do match - see 600 * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> 601 * 602 * <p>A <code>null</code> throwable returns <code>-1</code>. 603 * A <code>null</code> type returns <code>-1</code>. 604 * No match in the chain returns <code>-1</code>.</p> 605 * 606 * @param throwable the throwable to inspect, may be null 607 * @param type the type to search for, subclasses match, null returns -1 608 * @return the index into the throwable chain, -1 if no match or null input 609 * @since 2.1 610 */ 611 public static int indexOfType(Throwable throwable, Class type) { 612 return indexOf(throwable, type, 0, true); 613 } 614 615 /** 616 * <p>Returns the (zero based) index of the first <code>Throwable</code> 617 * that matches the specified type in the exception chain from 618 * a specified index. 619 * Subclasses of the specified class do match - see 620 * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> 621 * 622 * <p>A <code>null</code> throwable returns <code>-1</code>. 623 * A <code>null</code> type returns <code>-1</code>. 624 * No match in the chain returns <code>-1</code>. 625 * A negative start index is treated as zero. 626 * A start index greater than the number of throwables returns <code>-1</code>.</p> 627 * 628 * @param throwable the throwable to inspect, may be null 629 * @param type the type to search for, subclasses match, null returns -1 630 * @param fromIndex the (zero based) index of the starting position, 631 * negative treated as zero, larger than chain size returns -1 632 * @return the index into the throwable chain, -1 if no match or null input 633 * @since 2.1 634 */ 635 public static int indexOfType(Throwable throwable, Class type, int fromIndex) { 636 return indexOf(throwable, type, fromIndex, true); 637 } 638 639 /** 640 * <p>Worker method for the <code>indexOfType</code> methods.</p> 641 * 642 * @param throwable the throwable to inspect, may be null 643 * @param type the type to search for, subclasses match, null returns -1 644 * @param fromIndex the (zero based) index of the starting position, 645 * negative treated as zero, larger than chain size returns -1 646 * @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares 647 * using references 648 * @return index of the <code>type</code> within throwables nested withing the specified <code>throwable</code> 649 */ 650 private static int indexOf(Throwable throwable, Class type, int fromIndex, boolean subclass) { 651 if (throwable == null || type == null) { 652 return -1; 653 } 654 if (fromIndex < 0) { 655 fromIndex = 0; 656 } 657 Throwable[] throwables = getThrowables(throwable); 658 if (fromIndex >= throwables.length) { 659 return -1; 660 } 661 if (subclass) { 662 for (int i = fromIndex; i < throwables.length; i++) { 663 if (type.isAssignableFrom(throwables[i].getClass())) { 664 return i; 665 } 666 } 667 } else { 668 for (int i = fromIndex; i < throwables.length; i++) { 669 if (type.equals(throwables[i].getClass())) { 670 return i; 671 } 672 } 673 } 674 return -1; 675 } 676 677 /** 678 * <p>Removes common frames from the cause trace given the two stack traces.</p> 679 * 680 * @param causeFrames stack trace of a cause throwable 681 * @param wrapperFrames stack trace of a wrapper throwable 682 * @throws IllegalArgumentException if either argument is null 683 * @since 2.0 684 */ 685 public static void removeCommonFrames(List causeFrames, List wrapperFrames) { 686 if (causeFrames == null || wrapperFrames == null) { 687 throw new IllegalArgumentException("The List must not be null"); 688 } 689 int causeFrameIndex = causeFrames.size() - 1; 690 int wrapperFrameIndex = wrapperFrames.size() - 1; 691 while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { 692 // Remove the frame from the cause trace if it is the same 693 // as in the wrapper trace 694 String causeFrame = (String) causeFrames.get(causeFrameIndex); 695 String wrapperFrame = (String) wrapperFrames.get(wrapperFrameIndex); 696 if (causeFrame.equals(wrapperFrame)) { 697 causeFrames.remove(causeFrameIndex); 698 } 699 causeFrameIndex--; 700 wrapperFrameIndex--; 701 } 702 } 703 704 //----------------------------------------------------------------------- 705 /** 706 * <p>A way to get the entire nested stack-trace of an throwable.</p> 707 * 708 * <p>The result of this method is highly dependent on the JDK version 709 * and whether the exceptions override printStackTrace or not.</p> 710 * 711 * @param throwable the <code>Throwable</code> to be examined 712 * @return the nested stack trace, with the root cause first 713 * @since 2.0 714 */ 715 public static String getFullStackTrace(Throwable throwable) { 716 StringWriter sw = new StringWriter(); 717 PrintWriter pw = new PrintWriter(sw, true); 718 Throwable[] ts = getThrowables(throwable); 719 for (int i = 0; i < ts.length; i++) { 720 ts[i].printStackTrace(pw); 721 if (isNestedThrowable(ts[i])) { 722 break; 723 } 724 } 725 return sw.getBuffer().toString(); 726 } 727 728 //----------------------------------------------------------------------- 729 /** 730 * <p>Gets the stack trace from a Throwable as a String.</p> 731 * 732 * <p>The result of this method vary by JDK version as this method 733 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. 734 * On JDK1.3 and earlier, the cause exception will not be shown 735 * unless the specified throwable alters printStackTrace.</p> 736 * 737 * @param throwable the <code>Throwable</code> to be examined 738 * @return the stack trace as generated by the exception's 739 * <code>printStackTrace(PrintWriter)</code> method 740 */ 741 public static String getStackTrace(Throwable throwable) { 742 StringWriter sw = new StringWriter(); 743 PrintWriter pw = new PrintWriter(sw, true); 744 throwable.printStackTrace(pw); 745 return sw.getBuffer().toString(); 746 } 747 748 /** 749 * <p>Captures the stack trace associated with the specified 750 * <code>Throwable</code> object, decomposing it into a list of 751 * stack frames.</p> 752 * 753 * <p>The result of this method vary by JDK version as this method 754 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. 755 * On JDK1.3 and earlier, the cause exception will not be shown 756 * unless the specified throwable alters printStackTrace.</p> 757 * 758 * @param throwable the <code>Throwable</code> to examine, may be null 759 * @return an array of strings describing each stack frame, never null 760 */ 761// public static String[] getStackFrames(Throwable throwable) { 762// if (throwable == null) { 763// return ArrayUtils.EMPTY_STRING_ARRAY; 764// } 765// return getStackFrames(getStackTrace(throwable)); 766// } 767 768 //----------------------------------------------------------------------- 769 /** 770 * <p>Returns an array where each element is a line from the argument.</p> 771 * 772 * <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p> 773 * 774 * <p>Functionality shared between the 775 * <code>getStackFrames(Throwable)</code> methods of this and the 776 * {@link org.apache.commons.lang.exception.NestableDelegate} classes.</p> 777 * 778 * @param stackTrace a stack trace String 779 * @return an array where each element is a line from the argument 780 */ 781// static String[] getStackFrames(String stackTrace) { 782// String linebreak = SystemUtils.LINE_SEPARATOR; 783// StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); 784// List list = new ArrayList(); 785// while (frames.hasMoreTokens()) { 786// list.add(frames.nextToken()); 787// } 788// return toArray(list); 789// } 790 791 /** 792 * <p>Produces a <code>List</code> of stack frames - the message 793 * is not included. Only the trace of the specified exception is 794 * returned, any caused by trace is stripped.</p> 795 * 796 * <p>This works in most cases - it will only fail if the exception 797 * message contains a line that starts with: 798 * <code>" at".</code></p> 799 * 800 * @param t is any throwable 801 * @return List of stack frames 802 */ 803 static List getStackFrameList(Throwable t) { 804 String stackTrace = getStackTrace(t); 805 String linebreak = LINE_SEPARATOR; 806 StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); 807 List list = new ArrayList(); 808 boolean traceStarted = false; 809 while (frames.hasMoreTokens()) { 810 String token = frames.nextToken(); 811 // Determine if the line starts with <whitespace>at 812 int at = token.indexOf("at"); 813 if (at != -1 && token.substring(0, at).trim().length() == 0) { 814 traceStarted = true; 815 list.add(token); 816 } else if (traceStarted) { 817 break; 818 } 819 } 820 return list; 821 } 822 823 //----------------------------------------------------------------------- 824 /** 825 * Gets a short message summarising the exception. 826 * <p> 827 * The message returned is of the form 828 * {ClassNameWithoutPackage}: {ThrowableMessage} 829 * 830 * @param th the throwable to get a message for, null returns empty string 831 * @return the message, non-null 832 * @since Commons Lang 2.2 833 */ 834// public static String getMessage(Throwable th) { 835// if (th == null) { 836// return ""; 837// } 838// String clsName = ClassUtils.getShortClassName(th, null); 839// String msg = th.getMessage(); 840// return clsName + ": " + StringUtils.defaultString(msg); 841// } 842 843 //----------------------------------------------------------------------- 844 /** 845 * Gets a short message summarising the root cause exception. 846 * <p> 847 * The message returned is of the form 848 * {ClassNameWithoutPackage}: {ThrowableMessage} 849 * 850 * @param th the throwable to get a message for, null returns empty string 851 * @return the message, non-null 852 * @since Commons Lang 2.2 853 */ 854// public static String getRootCauseMessage(Throwable th) { 855// Throwable root = ExceptionUtils.getRootCause(th); 856// root = (root == null ? th : root); 857// return getMessage(root); 858// } 859 860 /** 861 * An interface to be implemented by {@link java.lang.Throwable} 862 * extensions which would like to be able to nest root exceptions 863 * inside themselves. 864 * 865 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a> 866 * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a> 867 * @author <a href="mailto:steven@caswell.name">Steven Caswell</a> 868 * @author Pete Gieser 869 * @since 1.0 870 * @version $Id$ 871 */ 872 public interface Nestable { 873 874 /** 875 * Returns the reference to the exception or error that caused the 876 * exception implementing the <code>Nestable</code> to be thrown. 877 * 878 * @return throwable that caused the original exception 879 */ 880 Throwable getCause(); 881 882 /** 883 * Returns the error message of this and any nested 884 * <code>Throwable</code>. 885 * 886 * @return the error message 887 */ 888 String getMessage(); 889 890 /** 891 * Returns the error message of the <code>Throwable</code> in the chain 892 * of <code>Throwable</code>s at the specified index, numbered from 0. 893 * 894 * @param index the index of the <code>Throwable</code> in the chain of 895 * <code>Throwable</code>s 896 * @return the error message, or null if the <code>Throwable</code> at the 897 * specified index in the chain does not contain a message 898 * @throws IndexOutOfBoundsException if the <code>index</code> argument is 899 * negative or not less than the count of <code>Throwable</code>s in the 900 * chain 901 */ 902 String getMessage(int index); 903 904 /** 905 * Returns the error message of this and any nested <code>Throwable</code>s 906 * in an array of Strings, one element for each message. Any 907 * <code>Throwable</code> not containing a message is represented in the 908 * array by a null. This has the effect of cause the length of the returned 909 * array to be equal to the result of the {@link #getThrowableCount()} 910 * operation. 911 * 912 * @return the error messages 913 */ 914 String[] getMessages(); 915 916 /** 917 * Returns the <code>Throwable</code> in the chain of 918 * <code>Throwable</code>s at the specified index, numbered from 0. 919 * 920 * @param index the index, numbered from 0, of the <code>Throwable</code> in 921 * the chain of <code>Throwable</code>s 922 * @return the <code>Throwable</code> 923 * @throws IndexOutOfBoundsException if the <code>index</code> argument is 924 * negative or not less than the count of <code>Throwable</code>s in the 925 * chain 926 */ 927 Throwable getThrowable(int index); 928 929 /** 930 * Returns the number of nested <code>Throwable</code>s represented by 931 * this <code>Nestable</code>, including this <code>Nestable</code>. 932 * 933 * @return the throwable count 934 */ 935 int getThrowableCount(); 936 937 /** 938 * Returns this <code>Nestable</code> and any nested <code>Throwable</code>s 939 * in an array of <code>Throwable</code>s, one element for each 940 * <code>Throwable</code>. 941 * 942 * @return the <code>Throwable</code>s 943 */ 944 Throwable[] getThrowables(); 945 946 /** 947 * Returns the index, numbered from 0, of the first occurrence of the 948 * specified type, or a subclass, in the chain of <code>Throwable</code>s. 949 * The method returns -1 if the specified type is not found in the chain. 950 * <p> 951 * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface 952 * such that this method matches subclasses. 953 * If you want to NOT match subclasses, please use 954 * (which is avaiable in all versions of lang). 955 * 956 * @param type the type to find, subclasses match, null returns -1 957 * @return index of the first occurrence of the type in the chain, or -1 if 958 * the type is not found 959 */ 960 int indexOfThrowable(Class type); 961 962 /** 963 * Returns the index, numbered from 0, of the first <code>Throwable</code> 964 * that matches the specified type, or a subclass, in the chain of <code>Throwable</code>s 965 * with an index greater than or equal to the specified index. 966 * The method returns -1 if the specified type is not found in the chain. 967 * <p> 968 * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface 969 * such that this method matches subclasses. 970 * If you want to NOT match subclasses, please use 971 * (which is avaiable in all versions of lang). 972 * 973 * @param type the type to find, subclasses match, null returns -1 974 * @param fromIndex the index, numbered from 0, of the starting position in 975 * the chain to be searched 976 * @return index of the first occurrence of the type in the chain, or -1 if 977 * the type is not found 978 * @throws IndexOutOfBoundsException if the <code>fromIndex</code> argument 979 * is negative or not less than the count of <code>Throwable</code>s in the 980 * chain 981 */ 982 int indexOfThrowable(Class type, int fromIndex); 983 984 /** 985 * Prints the stack trace of this exception to the specified print 986 * writer. Includes information from the exception, if any, 987 * which caused this exception. 988 * 989 * @param out <code>PrintWriter</code> to use for output. 990 */ 991 void printStackTrace(PrintWriter out); 992 993 /** 994 * Prints the stack trace of this exception to the specified print 995 * stream. Includes information from the exception, if any, 996 * which caused this exception. 997 * 998 * @param out <code>PrintStream</code> to use for output. 999 */ 1000 void printStackTrace(PrintStream out); 1001 1002 /** 1003 * Prints the stack trace for this exception only--root cause not 1004 * included--using the provided writer. Used by 1005 * individual stack traces to a buffer. The implementation of 1006 * this method should call 1007 * <code>super.printStackTrace(out);</code> in most cases. 1008 * 1009 * @param out The writer to use. 1010 */ 1011 void printPartialStackTrace(PrintWriter out); 1012 1013 } 1014}