diff --git a/rxjava-core/src/main/java/rx/util/CompositeException.java b/rxjava-core/src/main/java/rx/util/CompositeException.java index bca5dcfbf7..439b9400b2 100644 --- a/rxjava-core/src/main/java/rx/util/CompositeException.java +++ b/rxjava-core/src/main/java/rx/util/CompositeException.java @@ -18,7 +18,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Exception that is a composite of 1 or more other exceptions. @@ -84,9 +86,16 @@ private static String getStackTraceAsString(StackTraceElement[] stack) { return s.toString(); } - private static void attachCallingThreadStack(Throwable e, Throwable cause) { + /* package-private */ static void attachCallingThreadStack(Throwable e, Throwable cause) { + Set seenCauses = new HashSet(); + while (e.getCause() != null) { e = e.getCause(); + if (seenCauses.contains(e.getCause())) { + break; + } else { + seenCauses.add(e.getCause()); + } } // we now have 'e' as the last in the chain try { @@ -98,12 +107,13 @@ private static void attachCallingThreadStack(Throwable e, Throwable cause) { } } - private final static class CompositeExceptionCausalChain extends RuntimeException { + /* package-private */ final static class CompositeExceptionCausalChain extends RuntimeException { private static final long serialVersionUID = 3875212506787802066L; + /* package-private */ static String MESSAGE = "Chain of Causes for CompositeException In Order Received =>"; @Override public String getMessage() { - return "Chain of Causes for CompositeException In Order Received =>"; + return MESSAGE; } } diff --git a/rxjava-core/src/test/java/rx/util/CompositeExceptionTest.java b/rxjava-core/src/test/java/rx/util/CompositeExceptionTest.java new file mode 100644 index 0000000000..01cbeeb06b --- /dev/null +++ b/rxjava-core/src/test/java/rx/util/CompositeExceptionTest.java @@ -0,0 +1,92 @@ +/** + * Copyright 2013 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package rx.util; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class CompositeExceptionTest { + + private final Throwable ex1 = new Throwable("Ex1"); + private final Throwable ex2 = new Throwable("Ex2", ex1); + private final Throwable ex3 = new Throwable("Ex3", ex2); + + public CompositeExceptionTest() { + ex1.initCause(ex2); + } + + private CompositeException getNewCompositeExceptionWithEx123() { + List throwables = new ArrayList(); + throwables.add(ex1); + throwables.add(ex2); + throwables.add(ex3); + return new CompositeException(throwables); + } + + @Test(timeout = 1000) + public void testMultipleWithSameCause() { + Throwable rootCause = new Throwable("RootCause"); + Throwable e1 = new Throwable("1", rootCause); + Throwable e2 = new Throwable("2", rootCause); + Throwable e3 = new Throwable("3", rootCause); + CompositeException ce = new CompositeException("3 failures with same root cause", Arrays.asList(e1, e2, e3)); + } + + @Test(timeout = 1000) + public void testAttachCallingThreadStackParentThenChild() { + CompositeException.attachCallingThreadStack(ex1, ex2); + assertEquals("Ex2", ex1.getCause().getMessage()); + } + + @Test(timeout = 1000) + public void testAttachCallingThreadStackChildThenParent() { + CompositeException.attachCallingThreadStack(ex2, ex1); + assertEquals("Ex1", ex2.getCause().getMessage()); + } + + @Test(timeout = 1000) + public void testAttachCallingThreadStackAddComposite() { + CompositeException.attachCallingThreadStack(ex1, getNewCompositeExceptionWithEx123()); + assertEquals("Ex2", ex1.getCause().getMessage()); + } + + @Test(timeout = 1000) + public void testAttachCallingThreadStackAddToComposite() { + CompositeException compositeEx = getNewCompositeExceptionWithEx123(); + CompositeException.attachCallingThreadStack(compositeEx, ex1); + assertEquals(CompositeException.CompositeExceptionCausalChain.MESSAGE, compositeEx.getCause().getMessage()); + } + + @Test(timeout = 1000) + public void testAttachCallingThreadStackAddCompositeToItself() { + CompositeException compositeEx = getNewCompositeExceptionWithEx123(); + CompositeException.attachCallingThreadStack(compositeEx, compositeEx); + assertEquals(CompositeException.CompositeExceptionCausalChain.MESSAGE, compositeEx.getCause().getMessage()); + } + + @Test(timeout = 1000) + public void testAttachCallingThreadStackAddExceptionsToEachOther() { + CompositeException.attachCallingThreadStack(ex1, ex2); + CompositeException.attachCallingThreadStack(ex2, ex1); + assertEquals("Ex2", ex1.getCause().getMessage()); + assertEquals("Ex1", ex2.getCause().getMessage()); + } +} \ No newline at end of file