Skip to content

Commit e676ddd

Browse files
Merge pull request #892 from benjchristensen/onErrorFlatMap
onErrorFlatMap + OnErrorThrowable
2 parents d53f73b + 4328276 commit e676ddd

16 files changed

+430
-144
lines changed

rxjava-core/src/main/java/rx/Observable.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.concurrent.TimeUnit;
2626

2727
import rx.exceptions.Exceptions;
28+
import rx.exceptions.OnErrorThrowable;
2829
import rx.exceptions.OnErrorNotImplementedException;
2930
import rx.functions.Action0;
3031
import rx.functions.Action1;
@@ -79,16 +80,13 @@
7980
import rx.operators.OperationMergeMaxConcurrent;
8081
import rx.operators.OperationMinMax;
8182
import rx.operators.OperationMulticast;
82-
import rx.operators.OperationOnErrorResumeNextViaFunction;
8383
import rx.operators.OperationOnErrorResumeNextViaObservable;
8484
import rx.operators.OperationOnErrorReturn;
8585
import rx.operators.OperationOnExceptionResumeNextViaObservable;
8686
import rx.operators.OperationParallelMerge;
8787
import rx.operators.OperationReplay;
8888
import rx.operators.OperationRetry;
8989
import rx.operators.OperationSample;
90-
import rx.operators.OperatorObserveOnBounded;
91-
import rx.operators.OperatorScan;
9290
import rx.operators.OperationSequenceEqual;
9391
import rx.operators.OperationSingle;
9492
import rx.operators.OperationSkip;
@@ -117,8 +115,11 @@
117115
import rx.operators.OperatorMap;
118116
import rx.operators.OperatorMerge;
119117
import rx.operators.OperatorObserveOn;
118+
import rx.operators.OperatorOnErrorResumeNextViaFunction;
119+
import rx.operators.OperatorOnErrorFlatMap;
120120
import rx.operators.OperatorParallel;
121121
import rx.operators.OperatorRepeat;
122+
import rx.operators.OperatorScan;
122123
import rx.operators.OperatorSubscribeOn;
123124
import rx.operators.OperatorTake;
124125
import rx.operators.OperatorTimeout;
@@ -5209,7 +5210,7 @@ public final Boolean call(T t) {
52095210
* @see <a href="https://github.com/Netflix/RxJava/wiki/Error-Handling-Operators#wiki-onerrorresumenext">RxJava Wiki: onErrorResumeNext()</a>
52105211
*/
52115212
public final Observable<T> onErrorResumeNext(final Func1<Throwable, ? extends Observable<? extends T>> resumeFunction) {
5212-
return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction));
5213+
return lift(new OperatorOnErrorResumeNextViaFunction<T>(resumeFunction));
52135214
}
52145215

52155216
/**
@@ -5267,6 +5268,15 @@ public final Observable<T> onErrorReturn(Func1<Throwable, ? extends T> resumeFun
52675268
return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction));
52685269
}
52695270

5271+
/**
5272+
* Allows inserting onNext events into a stream when onError events are received
5273+
* and continuing the original sequence instead of terminating. Thus it allows a sequence
5274+
* with multiple onError events.
5275+
*/
5276+
public final Observable<T> onErrorFlatMap(final Func1<OnErrorThrowable, ? extends Observable<? extends T>> resumeFunction) {
5277+
return lift(new OperatorOnErrorFlatMap<T>(resumeFunction));
5278+
}
5279+
52705280
/**
52715281
* Instruct an Observable to pass control to another Observable rather than invoking
52725282
* {@link Observer#onError onError} if it encounters an {@link java.lang.Exception}.

rxjava-core/src/main/java/rx/exceptions/Exceptions.java

+44
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package rx.exceptions;
1717

18+
import java.util.HashSet;
19+
import java.util.Set;
20+
1821
public class Exceptions {
1922
private Exceptions() {
2023

@@ -53,4 +56,45 @@ else if (t instanceof StackOverflowError) {
5356
throw (LinkageError) t;
5457
}
5558
}
59+
60+
private static final int MAX_DEPTH = 25;
61+
62+
public static void addCause(Throwable e, Throwable cause) {
63+
Set<Throwable> seenCauses = new HashSet<Throwable>();
64+
65+
int i = 0;
66+
while (e.getCause() != null) {
67+
if (i++ >= MAX_DEPTH) {
68+
// stack too deep to associate cause
69+
return;
70+
}
71+
e = e.getCause();
72+
if (seenCauses.contains(e.getCause())) {
73+
break;
74+
} else {
75+
seenCauses.add(e.getCause());
76+
}
77+
}
78+
// we now have 'e' as the last in the chain
79+
try {
80+
e.initCause(cause);
81+
} catch (Throwable t) {
82+
// ignore
83+
// the javadocs say that some Throwables (depending on how they're made) will never
84+
// let me call initCause without blowing up even if it returns null
85+
}
86+
}
87+
88+
public static Throwable getFinalCause(Throwable e) {
89+
int i = 0;
90+
while (e.getCause() != null) {
91+
if (i++ >= MAX_DEPTH) {
92+
// stack too deep to get final cause
93+
return new RuntimeException("Stack too deep to get final cause");
94+
}
95+
e = e.getCause();
96+
}
97+
return e;
98+
}
99+
56100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* Copyright 2014 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package rx.exceptions;
17+
18+
public class OnErrorThrowable extends RuntimeException {
19+
20+
private static final long serialVersionUID = -569558213262703934L;
21+
22+
private final boolean hasValue;
23+
private final Object value;
24+
25+
private OnErrorThrowable(Throwable exception) {
26+
super(exception);
27+
hasValue = false;
28+
this.value = null;
29+
}
30+
31+
private OnErrorThrowable(Throwable exception, Object value) {
32+
super(exception);
33+
hasValue = true;
34+
this.value = value;
35+
}
36+
37+
public Object getValue() {
38+
return value;
39+
}
40+
41+
public boolean isValueNull() {
42+
return hasValue;
43+
}
44+
45+
public static OnErrorThrowable from(Throwable t) {
46+
Throwable cause = Exceptions.getFinalCause(t);
47+
if (cause instanceof OnErrorThrowable.OnNextValue) {
48+
return new OnErrorThrowable(t, ((OnNextValue) cause).getValue());
49+
} else {
50+
return new OnErrorThrowable(t);
51+
}
52+
}
53+
54+
/**
55+
* Adds the given value as the final cause of the given Throwable wrapped in OnNextValue RuntimeException..
56+
*
57+
* @param e
58+
* @param value
59+
* @return Throwable e passed in
60+
*/
61+
public static Throwable addValueAsLastCause(Throwable e, Object value) {
62+
Exceptions.addCause(e, new OnNextValue(value));
63+
return e;
64+
}
65+
66+
public static class OnNextValue extends RuntimeException {
67+
68+
private static final long serialVersionUID = -3454462756050397899L;
69+
private final Object value;
70+
71+
public OnNextValue(Object value) {
72+
super("OnError while emitting onNext value: " + value);
73+
this.value = value;
74+
}
75+
76+
public Object getValue() {
77+
return value;
78+
}
79+
80+
}
81+
}

rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java

-119
This file was deleted.

rxjava-core/src/main/java/rx/operators/OperatorCast.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
import rx.Observable.Operator;
1919
import rx.Subscriber;
20-
20+
import rx.exceptions.Exceptions;
21+
import rx.exceptions.OnErrorThrowable;
2122

2223
/**
2324
* Converts the elements of an observable sequence to the specified type.
@@ -46,7 +47,11 @@ public void onError(Throwable e) {
4647

4748
@Override
4849
public void onNext(T t) {
49-
o.onNext(castClass.cast(t));
50+
try {
51+
o.onNext(castClass.cast(t));
52+
} catch (Throwable e) {
53+
onError(OnErrorThrowable.addValueAsLastCause(e, t));
54+
}
5055
}
5156
};
5257
}

rxjava-core/src/main/java/rx/operators/OperatorDoOnEach.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import rx.Observable.Operator;
1919
import rx.Observer;
2020
import rx.Subscriber;
21+
import rx.exceptions.OnErrorThrowable;
2122

2223
/**
2324
* Converts the elements of an observable sequence to the specified type.
@@ -59,7 +60,7 @@ public void onNext(T value) {
5960
try {
6061
doOnEachObserver.onNext(value);
6162
} catch (Throwable e) {
62-
onError(e);
63+
onError(OnErrorThrowable.addValueAsLastCause(e, value));
6364
return;
6465
}
6566
observer.onNext(value);

rxjava-core/src/main/java/rx/operators/OperatorFilter.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
import rx.Observable.Operator;
1919
import rx.Subscriber;
20+
import rx.exceptions.OnErrorThrowable;
2021
import rx.functions.Func1;
21-
import rx.observables.GroupedObservable;
2222

2323
/**
2424
* Filters an Observable by discarding any items it emits that do not meet some test.
@@ -48,13 +48,13 @@ public void onError(Throwable e) {
4848
}
4949

5050
@Override
51-
public void onNext(T value) {
51+
public void onNext(T t) {
5252
try {
53-
if (predicate.call(value)) {
54-
child.onNext(value);
53+
if (predicate.call(t)) {
54+
child.onNext(t);
5555
}
56-
} catch (Throwable ex) {
57-
child.onError(ex);
56+
} catch (Throwable e) {
57+
child.onError(OnErrorThrowable.addValueAsLastCause(e, t));
5858
}
5959
}
6060

0 commit comments

Comments
 (0)