diff --git a/src/java/org/apache/commons/lang/exception/ExceptionUtils.java b/src/java/org/apache/commons/lang/exception/ExceptionUtils.java index 2964fb8b3..e435db093 100644 --- a/src/java/org/apache/commons/lang/exception/ExceptionUtils.java +++ b/src/java/org/apache/commons/lang/exception/ExceptionUtils.java @@ -67,6 +67,7 @@ import java.util.StringTokenizer; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; @@ -79,7 +80,7 @@ * @author Stephen Colebourne * @author Gary Gregory * @since 1.0 - * @version $Id: ExceptionUtils.java,v 1.28 2003/07/26 00:43:08 ggregory Exp $ + * @version $Id: ExceptionUtils.java,v 1.29 2003/07/26 13:05:21 scolebourne Exp $ */ public class ExceptionUtils { @@ -92,8 +93,7 @@ public class ExceptionUtils { static final String WRAPPED_MARKER = " [wrapped] "; /** - *
The names of methods commonly used to access a wrapped - * exception.
+ *The names of methods commonly used to access a wrapped exception.
*/ private static String[] CAUSE_METHOD_NAMES = { "getCause", @@ -107,12 +107,27 @@ public class ExceptionUtils { }; /** - *Constructs a new ExceptionUtils
. Protected to
- * discourage instantiation.
The Method object for JDK1.4 getCause.
*/ - protected ExceptionUtils() { + private static final Method THROWABLE_CAUSE_METHOD; + static { + Method getCauseMethod; + try { + getCauseMethod = Throwable.class.getMethod("getCause", null); + } catch (Exception e) { + getCauseMethod = null; + } + THROWABLE_CAUSE_METHOD = getCauseMethod; + } + + /** + *Public constructor allows an instance of ExceptionUtils
+ * to be created, although that is not normally necessary.
Adds to the list of method names used in the search for Throwable
* objects.
Introspects the specified Throwable
to obtain the cause.
Introspects the Throwable
to obtain the cause.
The method searches for methods with specific names that return a
* Throwable
object. This will pick up most wrapping exceptions,
@@ -154,31 +169,47 @@ public static void addCauseMethodName(String methodName) {
*
*
If none of the above is found, returns null
.
Throwable
.
- * @throws NullPointerException if the throwable is null
+ * @param throwable the throwable to introspect for a cause, may be null
+ * @return the cause of the Throwable
,
+ * null
if none found or null throwable input
*/
public static Throwable getCause(Throwable throwable) {
return getCause(throwable, CAUSE_METHOD_NAMES);
}
/**
- * Introspects the specified Throwable
to obtain the cause
- * using a supplied array of method names.
Introspects the Throwable
to obtain the cause.
A null
set of method names means use the default set.
+ * A null
in the set of method names will be ignored.
Throwable
.
- * @throws NullPointerException if the method names array is null
- * or contains null
- * @throws NullPointerException if the throwable is null
+ * @param throwable the throwable to introspect for a cause, may be null
+ * @param methodNames the method names, null treated as default set
+ * @return the cause of the Throwable
,
+ * null
if none found or null throwable input
*/
public static Throwable getCause(Throwable throwable, String[] methodNames) {
+ if (throwable == null) {
+ return null;
+ }
Throwable cause = getCauseUsingWellKnownTypes(throwable);
if (cause == null) {
+ if (methodNames == null) {
+ methodNames = CAUSE_METHOD_NAMES;
+ }
for (int i = 0; i < methodNames.length; i++) {
- cause = getCauseUsingMethodName(throwable, methodNames[i]);
- if (cause != null) {
- break;
+ String methodName = methodNames[i];
+ if (methodName != null) {
+ cause = getCauseUsingMethodName(throwable, methodName);
+ if (cause != null) {
+ break;
+ }
}
}
@@ -190,12 +221,15 @@ public static Throwable getCause(Throwable throwable, String[] methodNames) {
}
/**
- * Walks through the exception chain to the last element -- the - * "root" of the tree -- using {@link #getCause(Throwable)}, and + *
Introspects the Throwable
to obtain the root cause.
This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and * returns that exception.
* - * @param throwable the throwable to get the root cause for - * @return The root cause of theThrowable
.
+ * @param throwable the throwable to get the root cause for, may be null
+ * @return the root cause of the Throwable
,
+ * null
if none found or null throwable input
*/
public static Throwable getRootCause(Throwable throwable) {
Throwable cause = getCause(throwable);
@@ -209,13 +243,14 @@ public static Throwable getRootCause(Throwable throwable) {
}
/**
+ * Finds a Throwable
for known types.
Uses instanceof
checks to examine the exception,
* looking for well known types which could contain chained or
* wrapped exceptions.
null
if not
- * found.
+ * @return the wrapped exception, or null
if not found
*/
private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
if (throwable instanceof Nestable) {
@@ -234,8 +269,7 @@ private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
*
* @param throwable the exception to examine
* @param methodName the name of the method to find and invoke
- * @return The wrapped exception, or null
if not
- * found.
+ * @return the wrapped exception, or null
if not found
*/
private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
Method method = null;
@@ -261,8 +295,7 @@ private static Throwable getCauseUsingMethodName(Throwable throwable, String met
*
* @param throwable the exception to examine
* @param fieldName the name of the attribute to examine
- * @return The wrapped exception, or null
if not
- * found.
+ * @return the wrapped exception, or null
if not found
*/
private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) {
Field field = null;
@@ -282,15 +315,78 @@ private static Throwable getCauseUsingFieldName(Throwable throwable, String fiel
return null;
}
+ //-----------------------------------------------------------------------
/**
- * Returns the number of Throwable
objects in the
- * exception chain.
Checks if the Throwable class has a getCause
method.
This is true for JDK 1.4 and above.
+ * + * @return true if Throwable is nestable + */ + public static boolean isThrowableNested() { + return (THROWABLE_CAUSE_METHOD != null); + } + + /** + *Checks whether this Throwable
class can store a cause.
This method does not check whether it actually does store a cause.
*
- * @param throwable the exception to inspect
- * @return The throwable count.
+ * @param throwable the Throwable
to examine, may be null
+ * @return boolean true
if nested otherwise false
+ */
+ public static boolean isNestedThrowable(Throwable throwable) {
+ if (throwable == null) {
+ return false;
+ }
+
+ if (throwable instanceof Nestable) {
+ return true;
+ } else if (throwable instanceof SQLException) {
+ return true;
+ } else if (throwable instanceof InvocationTargetException) {
+ return true;
+ } else if (isThrowableNested()) {
+ return true;
+ }
+
+ Class cls = throwable.getClass();
+ for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) {
+ try {
+ Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null);
+ if (method != null) {
+ return true;
+ }
+ } catch (NoSuchMethodException ignored) {
+ } catch (SecurityException ignored) {
+ }
+ }
+
+ try {
+ Field field = cls.getField("detail");
+ if (field != null) {
+ return true;
+ }
+ } catch (NoSuchFieldException ignored) {
+ } catch (SecurityException ignored) {
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ *
Counts the number of Throwable
objects in the
+ * exception chain.
A throwable without cause will return 1
.
+ * A throwable with one cause will return 2
and so on.
+ * A null
throwable will return 0
.
Returns the list of Throwable
objects in the
* exception chain.
A throwable without cause will return an array containing
+ * one element - the input throwable.
+ * A throwable with one cause will return an array containing
+ * two elements. - the input throwable and the cause throwable.
+ * A null
throwable will return an array size zero.
Throwable
objects.
+ * @param throwable the throwable to inspect, may be null
+ * @return the array of throwables, never null
*/
public static Throwable[] getThrowables(Throwable throwable) {
List list = new ArrayList();
@@ -315,40 +417,50 @@ public static Throwable[] getThrowables(Throwable throwable) {
return (Throwable[]) list.toArray(new Throwable[list.size()]);
}
+ //-----------------------------------------------------------------------
/**
- * Delegates to {@link #indexOfThrowable(Throwable, Class, int)}, - * starting the search at the beginning of the exception chain.
+ *Returns the (zero based) index of the first Throwable
+ * that matches the specified type in the exception chain.
A null
throwable returns -1
.
+ * A null
type returns -1
.
+ * No match in the chain returns -1
.
Returns the (zero based) index, of the first
- * Throwable
that matches the specified type in the
- * exception chain of Throwable
objects with an index
- * greater than or equal to the specified index, or
- * -1
if the type is not found.
Returns the (zero based) index of the first Throwable
+ * that matches the specified type in the exception chain from
+ * a specified index.
A null
throwable returns -1
.
+ * A null
type returns -1
.
+ * No match in the chain returns -1
.
+ * A negative start index is treated as zero.
+ * A start index greater than the number of throwables returns -1
.
Class
to look for
- * @param fromIndex the (zero based) index of the starting
- * position in the chain to be searched
- * @return the first occurrence of the type in the chain, or
- * -1
if the type is not found
- * @throws IndexOutOfBoundsException If the fromIndex
- * argument is negative or not less than the count of
- * Throwable
s in the chain.
+ * @param throwable the throwable to inspect, may be null
+ * @param type the type to search for
+ * @param fromIndex the (zero based) index of the starting position,
+ * negative treated as zero, larger than chain size returns -1
+ * @return the index into the throwable chain, -1 if no match or null input
*/
public static int indexOfThrowable(Throwable throwable, Class type, int fromIndex) {
+ if (throwable == null) {
+ return -1;
+ }
if (fromIndex < 0) {
- throw new IndexOutOfBoundsException("Throwable index out of range: " + fromIndex);
+ fromIndex = 0;
}
Throwable[] throwables = ExceptionUtils.getThrowables(throwable);
if (fromIndex >= throwables.length) {
- throw new IndexOutOfBoundsException("Throwable index out of range: " + fromIndex);
+ return -1;
}
for (int i = fromIndex; i < throwables.length; i++) {
if (throwables[i].getClass().equals(type)) {
@@ -358,6 +470,25 @@ public static int indexOfThrowable(Throwable throwable, Class type, int fromInde
return -1;
}
+ //-----------------------------------------------------------------------
+ /**
+ * Prints a compact stack trace for the root cause of a throwable
+ * to System.err
.
The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.
+ * + *The method is equivalent to printStackTrace
for throwables
+ * that don't have nested causes.
Prints a compact stack trace for the root cause of a throwable.
* @@ -366,11 +497,21 @@ public static int indexOfThrowable(Throwable throwable, Class type, int fromInde * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc. * - *The method is equivalent to t.printStackTrace() for throwables + *
The method is equivalent to printStackTrace
for throwables
* that don't have nested causes.
null
*/
- public static void printRootCauseStackTrace(Throwable t, PrintStream stream) {
- String trace[] = getRootCauseStackTrace(t);
+ public static void printRootCauseStackTrace(Throwable throwable, PrintStream stream) {
+ if (throwable == null) {
+ return;
+ }
+ if (stream == null) {
+ throw new NullArgumentException("PrintStream");
+ }
+ String trace[] = getRootCauseStackTrace(throwable);
for (int i = 0; i < trace.length; i++) {
stream.println(trace[i]);
}
@@ -378,33 +519,47 @@ public static void printRootCauseStackTrace(Throwable t, PrintStream stream) {
}
/**
- * Calls printRootCauseStackTraceprintRootCauseStackTrace
.
printRootCauseStackTrace(t, System.err);+ *
Prints a compact stack trace for the root cause of a throwable.
+ * + *The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.
+ * + *The method is equivalent to printStackTrace
for throwables
+ * that don't have nested causes.
null
*/
- public static void printRootCauseStackTrace(Throwable t) {
- printRootCauseStackTrace(t, System.err);
- }
-
- /**
- * Same as {@link #printRootCauseStackTrace(Throwable,java.io.PrintStream)},
- * except it takes a PrintWriter
as an argument.
Creates a compact stack trace for the root cause of the supplied
* Throwable
.
Removes common frames from the cause trace given the two stack traces.
* * @param causeFrames stack trace of a cause throwable - * @param wrapperFrames stack trace of a wrapper throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null */ public static void removeCommonFrames(List causeFrames, List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new NullArgumentException("List"); + } int causeFrameIndex = causeFrames.size() - 1; int wrapperFrameIndex = wrapperFrames.size() - 1; while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { @@ -448,94 +607,54 @@ public static void removeCommonFrames(List causeFrames, List wrapperFrames) { } } + //----------------------------------------------------------------------- /** - *A convenient way of extracting the stack trace from an - * exception.
+ *Gets the stack trace from a Throwable as a String.
* - * @param t TheThrowable
.
- * @return The stack trace as generated by the exception's
- * printStackTrace(PrintWriter)
method.
+ * @param throwable the Throwable
to be examined
+ * @return the stack trace as generated by the exception's
+ * printStackTrace(PrintWriter)
method
*/
- public static String getStackTrace(Throwable t) {
+ public static String getStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
- t.printStackTrace(pw);
+ throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
/**
* A way to get the entire nested stack-trace of an throwable.
* - * @param t TheThrowable
.
- * @return The nested stack trace, with the root cause first.
+ * @param throwable the Throwable
to be examined
+ * @return the nested stack trace, with the root cause first
*/
- public static String getFullStackTrace(Throwable t) {
+ public static String getFullStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
- Throwable[] ts = getThrowables(t);
- for(int i=0; iThrowable
is considered nested
- * or not.
- *
- * @param throwable The Throwable
.
- * @return boolean true
if nested otherwise false
- */
- public static boolean isNestedThrowable(Throwable throwable) {
- if(throwable == null) {
- return false;
- }
-
- if (throwable instanceof Nestable) {
- return true;
- } else if (throwable instanceof SQLException) {
- return true;
- } else if (throwable instanceof InvocationTargetException) {
- return true;
- }
-
- int sz = CAUSE_METHOD_NAMES.length;
- for(int i=0; iThrowable
object, decomposing it into a list of
* stack frames.
*
- * @param t The Throwable
.
- * @return An array of strings describing each stack frame.
+ * @param throwable the Throwable
to exaamine, may be null
+ * @return an array of strings describing each stack frame, never null
*/
- public static String[] getStackFrames(Throwable t) {
- return getStackFrames(getStackTrace(t));
+ public static String[] getStackFrames(Throwable throwable) {
+ if (throwable == null) {
+ return ArrayUtils.EMPTY_STRING_ARRAY;
+ }
+ return getStackFrames(getStackTrace(throwable));
}
/**
@@ -551,8 +670,7 @@ static String[] getStackFrames(String stackTrace) {
while (frames.hasMoreTokens()) {
list.add(frames.nextToken());
}
- return (String[]) list.toArray(new String[] {
- });
+ return (String[]) list.toArray(new String[list.size()]);
}
/**
@@ -586,19 +704,4 @@ static List getStackFrameList(Throwable t) {
return list;
}
- private static Object getCauseMethod = null;
- static {
- try {
- getCauseMethod = Throwable.class.getMethod("getCause", null);
- } catch (Exception e) {
- // ignore
- }
- }
-
- /**
- * Checks if the Throwable class has a getCause
method.