Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@www.apache.org Received: (qmail 88997 invoked from network); 29 Mar 2006 22:08:32 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 29 Mar 2006 22:08:31 -0000 Received: (qmail 17258 invoked by uid 500); 29 Mar 2006 22:08:29 -0000 Delivered-To: apmail-jakarta-commons-dev-archive@jakarta.apache.org Received: (qmail 16948 invoked by uid 500); 29 Mar 2006 22:08:28 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 16937 invoked by uid 500); 29 Mar 2006 22:08:28 -0000 Received: (qmail 16934 invoked by uid 99); 29 Mar 2006 22:08:28 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 29 Mar 2006 14:08:28 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Wed, 29 Mar 2006 14:08:27 -0800 Received: (qmail 88836 invoked by uid 65534); 29 Mar 2006 22:07:57 -0000 Message-ID: <20060329220757.88833.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r389900 - in /jakarta/commons/proper/lang/trunk/src: java/org/apache/commons/lang/exception/ExceptionUtils.java test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java Date: Wed, 29 Mar 2006 22:07:55 -0000 To: commons-cvs@jakarta.apache.org From: scolebourne@apache.org X-Mailer: svnmailer-1.0.7 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: scolebourne Date: Wed Mar 29 14:07:53 2006 New Revision: 389900 URL: http://svn.apache.org/viewcvs?rev=389900&view=rev Log: Fix infinite loops leading to OutOfMemory with circular cause chain bug 37038 Modified: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/exception/ExceptionUtils.java jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java Modified: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/exception/ExceptionUtils.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/exception/ExceptionUtils.java?rev=389900&r1=389899&r2=389900&view=diff ============================================================================== --- jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/exception/ExceptionUtils.java (original) +++ jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/exception/ExceptionUtils.java Wed Mar 29 14:07:53 2006 @@ -233,6 +233,7 @@ return ArrayUtils.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0; } + //----------------------------------------------------------------------- /** *

Introspects the Throwable to obtain the cause.

* @@ -319,19 +320,19 @@ * "root" of the tree, using {@link #getCause(Throwable)}, and * returns that exception.

* + *

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 the Throwable, * 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 @@ * 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 @@ * 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.

+ * 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; } //----------------------------------------------------------------------- Modified: jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java?rev=389900&r1=389899&r2=389900&view=diff ============================================================================== --- jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java (original) +++ jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/exception/ExceptionUtilsTestCase.java Wed Mar 29 14:07:53 2006 @@ -22,6 +22,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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)); } //----------------------------------------------------------------------- --------------------------------------------------------------------- To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: commons-dev-help@jakarta.apache.org