From 8d8fdc381122c13169c0f970ac731ff418e98616 Mon Sep 17 00:00:00 2001 From: Shixiong Zhu Date: Tue, 31 May 2016 20:13:01 -0700 Subject: [PATCH] 1.x: Use the correct Throwable to set the cause for CompositeException The cause of #3679 is we use a wrong Throwable (its cause has been set) to set the cause and `initCause` will throw an exception. Hence, the cause chain is not created correctly. In this PR, it searches the root cause (which doesn't have a cause) and use it to call `initCause`. --- .../rx/exceptions/CompositeException.java | 29 +++++++++++++-- .../rx/exceptions/CompositeExceptionTest.java | 37 +++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/main/java/rx/exceptions/CompositeException.java b/src/main/java/rx/exceptions/CompositeException.java index b093c41437..310cfab8ae 100644 --- a/src/main/java/rx/exceptions/CompositeException.java +++ b/src/main/java/rx/exceptions/CompositeException.java @@ -147,13 +147,12 @@ public synchronized Throwable getCause() { // we now have 'e' as the last in the chain try { chain.initCause(e); - chain = chain.getCause(); } catch (Throwable t) { // ignore // the javadocs say that some Throwables (depending on how they're made) will never // let me call initCause without blowing up even if it returns null - chain = e; } + chain = getRootCause(chain); } cause = _cause; } @@ -280,12 +279,13 @@ public String getMessage() { private List getListOfCauses(Throwable ex) { List list = new ArrayList(); Throwable root = ex.getCause(); - if (root == null) { + if (root == null || root == ex) { return list; } else { while(true) { list.add(root); - if (root.getCause() == null) { + Throwable cause = root.getCause(); + if (cause == null || cause == root) { return list; } else { root = root.getCause(); @@ -293,4 +293,25 @@ private List getListOfCauses(Throwable ex) { } } } + + /** + * Returns the root cause of {@code e}. If {@code e.getCause()} returns {@null} or {@code e}, just return {@code e} itself. + * + * @param e the {@link Throwable} {@code e}. + * @return The root cause of {@code e}. If {@code e.getCause()} returns {@null} or {@code e}, just return {@code e} itself. + */ + private Throwable getRootCause(Throwable e) { + Throwable root = e.getCause(); + if (root == null || root == e) { + return e; + } else { + while(true) { + Throwable cause = root.getCause(); + if (cause == null || cause == root) { + return root; + } + root = root.getCause(); + } + } + } } diff --git a/src/test/java/rx/exceptions/CompositeExceptionTest.java b/src/test/java/rx/exceptions/CompositeExceptionTest.java index cba638e566..9cfc7fa6dd 100644 --- a/src/test/java/rx/exceptions/CompositeExceptionTest.java +++ b/src/test/java/rx/exceptions/CompositeExceptionTest.java @@ -28,6 +28,8 @@ import org.junit.Test; +import rx.exceptions.CompositeException.CompositeExceptionCausalChain; + public class CompositeExceptionTest { private final Throwable ex1 = new Throwable("Ex1"); @@ -239,4 +241,39 @@ public void messageVarargs() { CompositeException compositeException = new CompositeException(ex1, ex2, ex3); assertEquals("3 exceptions occurred. ", compositeException.getMessage()); } + + @Test + public void complexCauses() { + Throwable e1 = new Throwable("1"); + Throwable e2 = new Throwable("2"); + e1.initCause(e2); + + Throwable e3 = new Throwable("3"); + Throwable e4 = new Throwable("4"); + e3.initCause(e4); + + Throwable e5 = new Throwable("5"); + Throwable e6 = new Throwable("6"); + e5.initCause(e6); + + CompositeException compositeException = new CompositeException(e1, e3, e5); + assert(compositeException.getCause() instanceof CompositeExceptionCausalChain); + + List causeChain = new ArrayList(); + Throwable cause = compositeException.getCause().getCause(); + while (cause != null) { + causeChain.add(cause); + cause = cause.getCause(); + } + // The original relations + // + // e1 -> e2 + // e3 -> e4 + // e5 -> e6 + // + // will be set to + // + // e1 -> e2 -> e3 -> e4 -> e5 -> e6 + assertEquals(Arrays.asList(e1, e2, e3, e4, e5, e6), causeChain); + } } \ No newline at end of file