Skip to content

Commit a45de22

Browse files
authored
1.x: fix assembly tracking replacing original exception (#4215)
* 1.x: fix assembly tracking replacing original exception * Rename attach() to attachTo()
1 parent 4d5515a commit a45de22

8 files changed

+92
-34
lines changed

src/main/java/rx/Observable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,7 @@ public static <T> Observable<T> from(T[] array) {
17121712
* You should call the AsyncEmitter's onNext, onError and onCompleted methods in a serialized fashion. The
17131713
* rest of its methods are threadsafe.
17141714
*
1715+
* @param <T> the element type
17151716
* @param asyncEmitter the emitter that is called when a Subscriber subscribes to the returned {@code Observable}
17161717
* @param backpressure the backpressure mode to apply if the downstream Subscriber doesn't request (fast) enough
17171718
* @return the new Observable instance

src/main/java/rx/Scheduler.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,10 @@ public long now() {
253253
* });
254254
* </pre>
255255
*
256-
* @param combine
257-
* @return
256+
* @param <S> a Scheduler and a Subscription
257+
* @param combine the function that takes a two-level nested Observable sequence of a Completable and returns
258+
* the Completable that will be subscribed to and should trigger the execution of the scheduled Actions.
259+
* @return the Scheduler with the customized execution behavior
258260
*/
259261
@SuppressWarnings("unchecked")
260262
@Experimental

src/main/java/rx/exceptions/AssemblyStackTraceException.java

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package rx.exceptions;
1717

18+
import java.util.*;
19+
1820
import rx.annotations.Experimental;
21+
import rx.plugins.RxJavaHooks;
1922

2023
/**
2124
* A RuntimeException that is stackless but holds onto a textual
@@ -27,16 +30,6 @@ public final class AssemblyStackTraceException extends RuntimeException {
2730
/** */
2831
private static final long serialVersionUID = 2038859767182585852L;
2932

30-
/**
31-
* Constructs an AssemblyStackTraceException with the given message and
32-
* a cause.
33-
* @param message the message
34-
* @param cause the cause
35-
*/
36-
public AssemblyStackTraceException(String message, Throwable cause) {
37-
super(message, cause);
38-
}
39-
4033
/**
4134
* Constructs an AssemblyStackTraceException with the given message.
4235
* @param message the message
@@ -49,4 +42,48 @@ public AssemblyStackTraceException(String message) {
4942
public synchronized Throwable fillInStackTrace() { // NOPMD
5043
return this;
5144
}
45+
46+
/**
47+
* Finds an empty cause slot and assigns itself to it.
48+
* @param exception the exception to start from
49+
*/
50+
public void attachTo(Throwable exception) {
51+
Set<Throwable> memory = new HashSet<Throwable>();
52+
53+
for (;;) {
54+
if (exception.getCause() == null) {
55+
exception.initCause(this);
56+
return;
57+
}
58+
59+
exception = exception.getCause();
60+
if (!memory.add(exception)) {
61+
// in case we run into a cycle, give up and report this to the hooks
62+
RxJavaHooks.onError(this);
63+
return;
64+
}
65+
}
66+
}
67+
68+
/**
69+
* Locate the first AssemblyStackTraceException in the causal chain of the
70+
* given Throwable (or it if it's one).
71+
* @param e the input throwable
72+
* @return the AssemblyStackTraceException located or null if not found
73+
*/
74+
public static AssemblyStackTraceException find(Throwable e) {
75+
Set<Throwable> memory = new HashSet<Throwable>();
76+
for (;;) {
77+
if (e instanceof AssemblyStackTraceException) {
78+
return (AssemblyStackTraceException)e;
79+
}
80+
if (e == null || e.getCause() == null) {
81+
return null;
82+
}
83+
e = e.getCause();
84+
if (!memory.add(e)) {
85+
return null;
86+
}
87+
}
88+
}
5289
}

src/main/java/rx/internal/operators/OnSubscribeOnAssembly.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public void onCompleted() {
115115

116116
@Override
117117
public void onError(Throwable e) {
118-
e = new AssemblyStackTraceException(stacktrace, e);
118+
new AssemblyStackTraceException(stacktrace).attachTo(e);
119119
actual.onError(e);
120120
}
121121

src/main/java/rx/internal/operators/OnSubscribeOnAssemblyCompletable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public void onCompleted() {
7171

7272
@Override
7373
public void onError(Throwable e) {
74-
e = new AssemblyStackTraceException(stacktrace, e);
74+
new AssemblyStackTraceException(stacktrace).attachTo(e);
7575
actual.onError(e);
7676
}
7777
}

src/main/java/rx/internal/operators/OnSubscribeOnAssemblySingle.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public OnAssemblySingleSubscriber(SingleSubscriber<? super T> actual, String sta
6161

6262
@Override
6363
public void onError(Throwable e) {
64-
e = new AssemblyStackTraceException(stacktrace, e);
64+
new AssemblyStackTraceException(stacktrace).attachTo(e);
6565
actual.onError(e);
6666
}
6767

src/main/java/rx/plugins/RxJavaHooks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ public static Func1<Operator, Operator> getOnSingleLift() {
974974
* <p>
975975
* Calling with a {@code null} parameter restores the default behavior:
976976
* the hook returns the same object.
977-
* @param onObservableLift the function that is called with original Operator and should
977+
* @param onCompletableLift the function that is called with original Operator and should
978978
* return an Operator instance.
979979
*/
980980
public static void setOnCompletableLift(Func1<CompletableOperator, CompletableOperator> onCompletableLift) {

src/test/java/rx/plugins/RxJavaHooksTest.java

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ public void assemblyTrackingObservable() {
6464

6565
createObservable().subscribe(ts);
6666

67-
ts.assertError(AssemblyStackTraceException.class);
67+
ts.assertError(TestException.class);
6868

6969
Throwable ex = ts.getOnErrorEvents().get(0);
7070

71-
assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException);
71+
AssemblyStackTraceException aste = AssemblyStackTraceException.find(ex);
7272

73-
assertTrue("" + ex, ex instanceof AssemblyStackTraceException);
73+
assertNotNull(aste);
7474

75-
assertTrue(ex.getMessage(), ex.getMessage().contains("createObservable"));
75+
assertTrue(aste.getMessage(), aste.getMessage().contains("createObservable"));
7676

7777
RxJavaHooks.clearAssemblyTracking();
7878

@@ -81,6 +81,12 @@ public void assemblyTrackingObservable() {
8181
createObservable().subscribe(ts);
8282

8383
ts.assertError(TestException.class);
84+
85+
ex = ts.getOnErrorEvents().get(0);
86+
87+
aste = AssemblyStackTraceException.find(ex);
88+
89+
assertNull(aste);
8490
} finally {
8591
RxJavaHooks.resetAssemblyTracking();
8692
}
@@ -103,15 +109,15 @@ public void assemblyTrackingSingle() {
103109

104110
createSingle().subscribe(ts);
105111

106-
ts.assertError(AssemblyStackTraceException.class);
112+
ts.assertError(TestException.class);
107113

108114
Throwable ex = ts.getOnErrorEvents().get(0);
109-
110-
assertTrue("" + ex, ex instanceof AssemblyStackTraceException);
111-
112-
assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException);
113-
114-
assertTrue(ex.getMessage(), ex.getMessage().contains("createSingle"));
115+
116+
AssemblyStackTraceException aste = AssemblyStackTraceException.find(ex);
117+
118+
assertNotNull(aste);
119+
120+
assertTrue(aste.getMessage(), aste.getMessage().contains("createSingle"));
115121

116122
RxJavaHooks.clearAssemblyTracking();
117123

@@ -120,6 +126,12 @@ public void assemblyTrackingSingle() {
120126
createSingle().subscribe(ts);
121127

122128
ts.assertError(TestException.class);
129+
130+
ex = ts.getOnErrorEvents().get(0);
131+
132+
aste = AssemblyStackTraceException.find(ex);
133+
134+
assertNull(aste);
123135
} finally {
124136
RxJavaHooks.resetAssemblyTracking();
125137
}
@@ -142,15 +154,15 @@ public void assemblyTrackingCompletable() {
142154

143155
createCompletable().subscribe(ts);
144156

145-
ts.assertError(AssemblyStackTraceException.class);
157+
ts.assertError(TestException.class);
146158

147159
Throwable ex = ts.getOnErrorEvents().get(0);
148-
149-
assertTrue("" + ex, ex instanceof AssemblyStackTraceException);
150-
151-
assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException);
152-
153-
assertTrue(ex.getMessage(), ex.getMessage().contains("createCompletable"));
160+
161+
AssemblyStackTraceException aste = AssemblyStackTraceException.find(ex);
162+
163+
assertNotNull(aste);
164+
165+
assertTrue(aste.getMessage(), aste.getMessage().contains("createCompletable"));
154166

155167
RxJavaHooks.clearAssemblyTracking();
156168

@@ -160,6 +172,12 @@ public void assemblyTrackingCompletable() {
160172

161173
ts.assertError(TestException.class);
162174

175+
ex = ts.getOnErrorEvents().get(0);
176+
177+
aste = AssemblyStackTraceException.find(ex);
178+
179+
assertNull(aste);
180+
163181
} finally {
164182
RxJavaHooks.resetAssemblyTracking();
165183
}

0 commit comments

Comments
 (0)