diff --git a/src/java/org/apache/commons/lang/exception/ExceptionUtils.java b/src/java/org/apache/commons/lang/exception/ExceptionUtils.java index 1e576ba9b..9d36217f9 100644 --- a/src/java/org/apache/commons/lang/exception/ExceptionUtils.java +++ b/src/java/org/apache/commons/lang/exception/ExceptionUtils.java @@ -233,6 +233,7 @@ public class ExceptionUtils { return ArrayUtils.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0; } + //----------------------------------------------------------------------- /** *
Introspects the Throwable
to obtain the cause.
From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.
+ * * @param throwable the throwable to get the root cause for, may be null * @return the root cause of theThrowable
,
* null
if none found or null throwable input
*/
public static Throwable getRootCause(Throwable throwable) {
- Throwable cause = getCause(throwable);
- if (cause != null) {
- throwable = cause;
- while ((throwable = getCause(throwable)) != null) {
- cause = throwable;
- }
- }
- return cause;
+ List list = getThrowableList(throwable);
+ return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1));
}
/**
@@ -490,16 +491,16 @@ public class ExceptionUtils {
* A throwable with one cause will return 2
and so on.
* A null
throwable will return 0
.
*
+ * From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.
+ * * @param throwable the throwable to inspect, may be null * @return the count of throwables, zero if null input */ public static int getThrowableCount(Throwable throwable) { - int count = 0; - while (throwable != null) { - count++; - throwable = ExceptionUtils.getCause(throwable); - } - return count; + return getThrowableList(throwable).size(); } /** @@ -510,18 +511,48 @@ public class ExceptionUtils { * one element - the input throwable. * A throwable with one cause will return an array containing * two elements. - the input throwable and the cause throwable. - * Anull
throwable will return an array size zero.
+ * A null
throwable will return an array of size zero.
*
+ * From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.
+ * + * @see #getThrowableList(Throwable) * @param throwable the throwable to inspect, may be null * @return the array of throwables, never null */ public static Throwable[] getThrowables(Throwable throwable) { + List list = getThrowableList(throwable); + return (Throwable[]) list.toArray(new Throwable[list.size()]); + } + + /** + *Returns the list of Throwable
objects in the
+ * exception chain.
A throwable without cause will return a list containing
+ * one element - the input throwable.
+ * A throwable with one cause will return a list containing
+ * two elements. - the input throwable and the cause throwable.
+ * A null
throwable will return a list of size zero.
This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.
+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { List list = new ArrayList(); - while (throwable != null) { + while (throwable != null && list.contains(throwable) == false) { list.add(throwable); throwable = ExceptionUtils.getCause(throwable); } - return (Throwable[]) list.toArray(new Throwable[list.size()]); + return list; } //----------------------------------------------------------------------- diff --git a/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java b/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java index 3b1cf32ab..f1d6035a0 100644 --- a/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java +++ b/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java @@ -22,6 +22,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; +import java.util.List; import junit.framework.Assert; import junit.framework.Test; @@ -43,6 +44,9 @@ public class ExceptionUtilsTestCase extends junit.framework.TestCase { private NestableException nested; private Throwable withCause; private Throwable withoutCause; + private Throwable jdkNoCause; + private ExceptionWithCause selfCause; + private ExceptionWithCause recursiveCause; public ExceptionUtilsTestCase(String name) { super(name); @@ -56,6 +60,22 @@ public class ExceptionUtilsTestCase extends junit.framework.TestCase { withoutCause = createExceptionWithoutCause(); nested = new NestableException(withoutCause); withCause = new ExceptionWithCause(nested); + jdkNoCause = new NullPointerException(); + selfCause = new ExceptionWithCause(null); + selfCause.setCause(selfCause); + ExceptionWithCause a = new ExceptionWithCause(null); + ExceptionWithCause b = new ExceptionWithCause(a); + a.setCause(b); + recursiveCause = new ExceptionWithCause(a); + } + + protected void tearDown() throws Exception { + withoutCause = null; + nested = null; + withCause = null; + jdkNoCause = null; + selfCause = null; + recursiveCause = null; } //----------------------------------------------------------------------- @@ -109,6 +129,11 @@ public class ExceptionUtilsTestCase extends junit.framework.TestCase { assertSame(null, ExceptionUtils.getCause(withoutCause)); assertSame(withoutCause, ExceptionUtils.getCause(nested)); assertSame(nested, ExceptionUtils.getCause(withCause)); + assertSame(null, ExceptionUtils.getCause(jdkNoCause)); + assertSame(selfCause, ExceptionUtils.getCause(selfCause)); + assertSame(recursiveCause.getCause(), ExceptionUtils.getCause(recursiveCause)); + assertSame(recursiveCause.getCause().getCause(), ExceptionUtils.getCause(recursiveCause.getCause())); + assertSame(recursiveCause.getCause(), ExceptionUtils.getCause(recursiveCause.getCause().getCause())); } public void testGetCause_ThrowableArray() { @@ -139,6 +164,9 @@ public class ExceptionUtilsTestCase extends junit.framework.TestCase { assertSame(null, ExceptionUtils.getRootCause(withoutCause)); assertSame(withoutCause, ExceptionUtils.getRootCause(nested)); assertSame(withoutCause, ExceptionUtils.getRootCause(withCause)); + assertSame(null, ExceptionUtils.getRootCause(jdkNoCause)); + assertSame(null, ExceptionUtils.getRootCause(selfCause)); + assertSame(recursiveCause.getCause().getCause(), ExceptionUtils.getRootCause(recursiveCause)); } public void testSetCause() { @@ -190,21 +218,102 @@ public class ExceptionUtilsTestCase extends junit.framework.TestCase { assertEquals(1, ExceptionUtils.getThrowableCount(withoutCause)); assertEquals(2, ExceptionUtils.getThrowableCount(nested)); assertEquals(3, ExceptionUtils.getThrowableCount(withCause)); + assertEquals(1, ExceptionUtils.getThrowableCount(jdkNoCause)); + assertEquals(1, ExceptionUtils.getThrowableCount(selfCause)); + assertEquals(3, ExceptionUtils.getThrowableCount(recursiveCause)); } - public void testGetThrowables_Throwable() { + //----------------------------------------------------------------------- + public void testGetThrowables_Throwable_null() { assertEquals(0, ExceptionUtils.getThrowables(null).length); - assertEquals(1, ExceptionUtils.getThrowables(withoutCause).length); - assertSame(withoutCause, ExceptionUtils.getThrowables(withoutCause)[0]); - - assertEquals(2, ExceptionUtils.getThrowables(nested).length); - assertSame(nested, ExceptionUtils.getThrowables(nested)[0]); - assertSame(withoutCause, ExceptionUtils.getThrowables(nested)[1]); - - assertEquals(3, ExceptionUtils.getThrowables(withCause).length); - assertSame(withCause, ExceptionUtils.getThrowables(withCause)[0]); - assertSame(nested, ExceptionUtils.getThrowables(withCause)[1]); - assertSame(withoutCause, ExceptionUtils.getThrowables(withCause)[2]); + } + + public void testGetThrowables_Throwable_withoutCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(withoutCause); + assertEquals(1, throwables.length); + assertSame(withoutCause, throwables[0]); + } + + public void testGetThrowables_Throwable_nested() { + Throwable[] throwables = ExceptionUtils.getThrowables(nested); + assertEquals(2, throwables.length); + assertSame(nested, throwables[0]); + assertSame(withoutCause, throwables[1]); + } + + public void testGetThrowables_Throwable_withCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(withCause); + assertEquals(3, throwables.length); + assertSame(withCause, throwables[0]); + assertSame(nested, throwables[1]); + assertSame(withoutCause, throwables[2]); + } + + public void testGetThrowables_Throwable_jdkNoCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(jdkNoCause); + assertEquals(1, throwables.length); + assertSame(jdkNoCause, throwables[0]); + } + + public void testGetThrowables_Throwable_selfCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(selfCause); + assertEquals(1, throwables.length); + assertSame(selfCause, throwables[0]); + } + + public void testGetThrowables_Throwable_recursiveCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(recursiveCause); + assertEquals(3, throwables.length); + assertSame(recursiveCause, throwables[0]); + assertSame(recursiveCause.getCause(), throwables[1]); + assertSame(recursiveCause.getCause().getCause(), throwables[2]); + } + + //----------------------------------------------------------------------- + public void testGetThrowableList_Throwable_null() { + List throwables = ExceptionUtils.getThrowableList(null); + assertEquals(0, throwables.size()); + } + + public void testGetThrowableList_Throwable_withoutCause() { + List throwables = ExceptionUtils.getThrowableList(withoutCause); + assertEquals(1, throwables.size()); + assertSame(withoutCause, throwables.get(0)); + } + + public void testGetThrowableList_Throwable_nested() { + List throwables = ExceptionUtils.getThrowableList(nested); + assertEquals(2, throwables.size()); + assertSame(nested, throwables.get(0)); + assertSame(withoutCause, throwables.get(1)); + } + + public void testGetThrowableList_Throwable_withCause() { + List throwables = ExceptionUtils.getThrowableList(withCause); + assertEquals(3, throwables.size()); + assertSame(withCause, throwables.get(0)); + assertSame(nested, throwables.get(1)); + assertSame(withoutCause, throwables.get(2)); + } + + public void testGetThrowableList_Throwable_jdkNoCause() { + List throwables = ExceptionUtils.getThrowableList(jdkNoCause); + assertEquals(1, throwables.size()); + assertSame(jdkNoCause, throwables.get(0)); + } + + public void testGetThrowableList_Throwable_selfCause() { + List throwables = ExceptionUtils.getThrowableList(selfCause); + assertEquals(1, throwables.size()); + assertSame(selfCause, throwables.get(0)); + } + + public void testGetThrowableList_Throwable_recursiveCause() { + List throwables = ExceptionUtils.getThrowableList(recursiveCause); + assertEquals(3, throwables.size()); + assertSame(recursiveCause, throwables.get(0)); + assertSame(recursiveCause.getCause(), throwables.get(1)); + assertSame(recursiveCause.getCause().getCause(), throwables.get(2)); } //-----------------------------------------------------------------------