81
81
import rx .util .functions .Func3 ;
82
82
import rx .util .functions .Func4 ;
83
83
import rx .util .functions .FuncN ;
84
+ import rx .util .functions .Function ;
84
85
import rx .util .functions .FunctionLanguageAdaptor ;
85
86
import rx .util .functions .Functions ;
86
87
@@ -152,7 +153,7 @@ public Subscription subscribe(Observer<T> observer) {
152
153
/**
153
154
* See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
154
155
*/
155
- if (observer . getClass (). getPackage (). getName (). startsWith ( "rx" )) {
156
+ if (isInternalImplementation ( observer )) {
156
157
Subscription s = onSubscribe .call (observer );
157
158
if (s == null ) {
158
159
// this generally shouldn't be the case on a 'trusted' onSubscribe but in case it happens
@@ -178,6 +179,16 @@ public Subscription subscribe(Observer<T> observer) {
178
179
}
179
180
}
180
181
182
+ /**
183
+ * Used for protecting against errors being thrown from Observer implementations and ensuring onNext/onError/onCompleted contract compliance.
184
+ * <p>
185
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
186
+ */
187
+ private Subscription protectivelyWrapAndSubscribe (Observer <T > o ) {
188
+ AtomicObservableSubscription subscription = new AtomicObservableSubscription ();
189
+ return subscription .wrap (subscribe (new AtomicObserver <T >(subscription , o )));
190
+ }
191
+
181
192
@ SuppressWarnings ({ "rawtypes" , "unchecked" })
182
193
public Subscription subscribe (final Map <String , Object > callbacks ) {
183
194
// lookup and memoize onNext
@@ -187,7 +198,12 @@ public Subscription subscribe(final Map<String, Object> callbacks) {
187
198
}
188
199
final FuncN onNext = Functions .from (_onNext );
189
200
190
- return subscribe (new Observer () {
201
+ /**
202
+ * Wrapping since raw functions provided by the user are being invoked.
203
+ *
204
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
205
+ */
206
+ return protectivelyWrapAndSubscribe (new Observer () {
191
207
192
208
public void onCompleted () {
193
209
Object onComplete = callbacks .get ("onCompleted" );
@@ -224,7 +240,12 @@ public Subscription subscribe(final Object o) {
224
240
}
225
241
final FuncN onNext = Functions .from (o );
226
242
227
- return subscribe (new Observer () {
243
+ /**
244
+ * Wrapping since raw functions provided by the user are being invoked.
245
+ *
246
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
247
+ */
248
+ return protectivelyWrapAndSubscribe (new Observer () {
228
249
229
250
public void onCompleted () {
230
251
// do nothing
@@ -244,7 +265,12 @@ public void onNext(Object args) {
244
265
245
266
public Subscription subscribe (final Action1 <T > onNext ) {
246
267
247
- return subscribe (new Observer <T >() {
268
+ /**
269
+ * Wrapping since raw functions provided by the user are being invoked.
270
+ *
271
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
272
+ */
273
+ return protectivelyWrapAndSubscribe (new Observer <T >() {
248
274
249
275
public void onCompleted () {
250
276
// do nothing
@@ -273,7 +299,12 @@ public Subscription subscribe(final Object onNext, final Object onError) {
273
299
}
274
300
final FuncN onNextFunction = Functions .from (onNext );
275
301
276
- return subscribe (new Observer () {
302
+ /**
303
+ * Wrapping since raw functions provided by the user are being invoked.
304
+ *
305
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
306
+ */
307
+ return protectivelyWrapAndSubscribe (new Observer () {
277
308
278
309
public void onCompleted () {
279
310
// do nothing
@@ -295,7 +326,12 @@ public void onNext(Object args) {
295
326
296
327
public Subscription subscribe (final Action1 <T > onNext , final Action1 <Exception > onError ) {
297
328
298
- return subscribe (new Observer <T >() {
329
+ /**
330
+ * Wrapping since raw functions provided by the user are being invoked.
331
+ *
332
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
333
+ */
334
+ return protectivelyWrapAndSubscribe (new Observer <T >() {
299
335
300
336
public void onCompleted () {
301
337
// do nothing
@@ -326,7 +362,12 @@ public Subscription subscribe(final Object onNext, final Object onError, final O
326
362
}
327
363
final FuncN onNextFunction = Functions .from (onNext );
328
364
329
- return subscribe (new Observer () {
365
+ /**
366
+ * Wrapping since raw functions provided by the user are being invoked.
367
+ *
368
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
369
+ */
370
+ return protectivelyWrapAndSubscribe (new Observer () {
330
371
331
372
public void onCompleted () {
332
373
if (onComplete != null ) {
@@ -350,7 +391,12 @@ public void onNext(Object args) {
350
391
351
392
public Subscription subscribe (final Action1 <T > onNext , final Action1 <Exception > onError , final Action0 onComplete ) {
352
393
353
- return subscribe (new Observer <T >() {
394
+ /**
395
+ * Wrapping since raw functions provided by the user are being invoked.
396
+ *
397
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
398
+ */
399
+ return protectivelyWrapAndSubscribe (new Observer <T >() {
354
400
355
401
public void onCompleted () {
356
402
onComplete .call ();
@@ -389,7 +435,12 @@ public void forEach(final Action1<T> onNext) {
389
435
final CountDownLatch latch = new CountDownLatch (1 );
390
436
final AtomicReference <Exception > exceptionFromOnError = new AtomicReference <Exception >();
391
437
392
- subscribe (new Observer <T >() {
438
+ /**
439
+ * Wrapping since raw functions provided by the user are being invoked.
440
+ *
441
+ * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
442
+ */
443
+ protectivelyWrapAndSubscribe (new Observer <T >() {
393
444
public void onCompleted () {
394
445
latch .countDown ();
395
446
}
@@ -3260,6 +3311,23 @@ public Iterable<T> mostRecent(T initialValue) {
3260
3311
return mostRecent (this , initialValue );
3261
3312
}
3262
3313
3314
+ /**
3315
+ * Whether a given {@link Function} is an internal implementation inside rx.* packages or not.
3316
+ * <p>
3317
+ * For why this is being used see https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator"
3318
+ *
3319
+ * NOTE: If strong reasons for not depending on package names comes up then the implementation of this method can change to looking for a marker interface.
3320
+ *
3321
+ * @param f
3322
+ * @return
3323
+ */
3324
+ private boolean isInternalImplementation (Object o ) {
3325
+ if (o == null ) {
3326
+ return true ;
3327
+ }
3328
+ return (o .getClass ().getPackage ().getName ().startsWith ("rx." ));
3329
+ }
3330
+
3263
3331
public static class UnitTest {
3264
3332
3265
3333
@ Mock
@@ -3723,6 +3791,39 @@ public void onNext(String v) {
3723
3791
}
3724
3792
}
3725
3793
3794
+ @ Test
3795
+ public void testForEachWithError () {
3796
+ try {
3797
+ Observable .create (new Func1 <Observer <String >, Subscription >() {
3798
+
3799
+ @ Override
3800
+ public Subscription call (final Observer <String > observer ) {
3801
+ final BooleanSubscription subscription = new BooleanSubscription ();
3802
+ new Thread (new Runnable () {
3803
+
3804
+ @ Override
3805
+ public void run () {
3806
+ observer .onNext ("one" );
3807
+ observer .onNext ("two" );
3808
+ observer .onNext ("three" );
3809
+ observer .onCompleted ();
3810
+ }
3811
+ }).start ();
3812
+ return subscription ;
3813
+ }
3814
+ }).forEach (new Action1 <String >() {
3815
+
3816
+ @ Override
3817
+ public void call (String t1 ) {
3818
+ throw new RuntimeException ("fail" );
3819
+ }
3820
+ });
3821
+ fail ("we expect an exception to be thrown" );
3822
+ } catch (Exception e ) {
3823
+ // do nothing as we expect this
3824
+ }
3825
+ }
3826
+
3726
3827
private static class TestException extends RuntimeException {
3727
3828
private static final long serialVersionUID = 1L ;
3728
3829
}
0 commit comments