82
82
import rx .subjects .Subject ;
83
83
import rx .subscriptions .BooleanSubscription ;
84
84
import rx .subscriptions .Subscriptions ;
85
+ import rx .util .OnErrorNotImplementedException ;
85
86
import rx .util .Range ;
86
87
import rx .util .Timestamped ;
87
88
import rx .util .functions .Action0 ;
@@ -116,14 +117,10 @@ public class Observable<T> {
116
117
117
118
private final Func1 <Observer <T >, Subscription > onSubscribe ;
118
119
119
- protected Observable () {
120
- this (null );
121
- }
122
-
123
120
/**
124
- * Construct an Observable with Function to execute when subscribed to.
121
+ * Observable with Function to execute when subscribed to.
125
122
* <p>
126
- * NOTE: Generally you're better off using {@link #create(Func1)} to create an Observable instead of using inheritance.
123
+ * NOTE: Use {@link #create(Func1)} to create an Observable instead of this method unless you specifically have a need for inheritance.
127
124
*
128
125
* @param onSubscribe
129
126
* {@link Func1} to be executed when {@link #subscribe(Observer)} is called.
@@ -132,6 +129,11 @@ protected Observable(Func1<Observer<T>, Subscription> onSubscribe) {
132
129
this .onSubscribe = onSubscribe ;
133
130
}
134
131
132
+ protected Observable () {
133
+ this (null );
134
+ //TODO should this be made private to prevent it? It really serves no good purpose and only confuses things. Unit tests are incorrectly using it today
135
+ }
136
+
135
137
/**
136
138
* an {@link Observer} must call an Observable's <code>subscribe</code> method in order to register itself
137
139
* to receive push-based notifications from the Observable. A typical implementation of the
@@ -156,11 +158,16 @@ protected Observable(Func1<Observer<T>, Subscription> onSubscribe) {
156
158
* @param observer
157
159
* @return a {@link Subscription} reference that allows observers
158
160
* to stop receiving notifications before the provider has finished sending them
161
+ * @throws IllegalArgumentException
162
+ * if null argument provided
159
163
*/
160
164
public Subscription subscribe (Observer <T > observer ) {
161
165
// allow the hook to intercept and/or decorate
162
166
Func1 <Observer <T >, Subscription > onSubscribeFunction = hook .onSubscribeStart (this , onSubscribe );
163
167
// validate and proceed
168
+ if (observer == null ) {
169
+ throw new IllegalArgumentException ("observer can not be null" );
170
+ }
164
171
if (onSubscribeFunction == null ) {
165
172
throw new IllegalStateException ("onSubscribe function can not be null." );
166
173
// the subscribe function can also be overridden but generally that's not the appropriate approach so I won't mention that in the exception
@@ -183,10 +190,16 @@ public Subscription subscribe(Observer<T> observer) {
183
190
subscription .wrap (onSubscribeFunction .call (new AtomicObserver <T >(subscription , observer )));
184
191
return hook .onSubscribeReturn (this , subscription );
185
192
}
193
+ } catch (OnErrorNotImplementedException e ) {
194
+ // special handling when onError is not implemented ... we just rethrow
195
+ throw e ;
186
196
} catch (Exception e ) {
187
197
// if an unhandled error occurs executing the onSubscribe we will propagate it
188
198
try {
189
199
observer .onError (hook .onSubscribeError (this , e ));
200
+ } catch (OnErrorNotImplementedException e2 ) {
201
+ // special handling when onError is not implemented ... we just rethrow
202
+ throw e2 ;
190
203
} catch (Exception e2 ) {
191
204
// if this happens it means the onError itself failed (perhaps an invalid function implementation)
192
205
// so we are unable to propagate the error correctly and will just throw
@@ -223,6 +236,8 @@ public Subscription subscribe(Observer<T> observer) {
223
236
* The {@link Scheduler} that the sequence is subscribed to on.
224
237
* @return a {@link Subscription} reference that allows observers
225
238
* to stop receiving notifications before the provider has finished sending them
239
+ * @throws IllegalArgumentException
240
+ * if null argument provided
226
241
*/
227
242
public Subscription subscribe (Observer <T > observer , Scheduler scheduler ) {
228
243
return subscribeOn (scheduler ).subscribe (observer );
@@ -240,11 +255,14 @@ private Subscription protectivelyWrapAndSubscribe(Observer<T> o) {
240
255
241
256
@ SuppressWarnings ({ "rawtypes" , "unchecked" })
242
257
public Subscription subscribe (final Map <String , Object > callbacks ) {
243
- // lookup and memoize onNext
258
+ if (callbacks == null ) {
259
+ throw new RuntimeException ("callbacks map can not be null" );
260
+ }
244
261
Object _onNext = callbacks .get ("onNext" );
245
262
if (_onNext == null ) {
246
- throw new RuntimeException ("onNext must be implemented " );
263
+ throw new RuntimeException ("' onNext' key must contain an implementation " );
247
264
}
265
+ // lookup and memoize onNext
248
266
final FuncN onNext = Functions .from (_onNext );
249
267
250
268
/**
@@ -268,6 +286,8 @@ public void onError(Exception e) {
268
286
Object onError = callbacks .get ("onError" );
269
287
if (onError != null ) {
270
288
Functions .from (onError ).call (e );
289
+ } else {
290
+ throw new OnErrorNotImplementedException (e );
271
291
}
272
292
}
273
293
@@ -290,10 +310,11 @@ public Subscription subscribe(final Object o) {
290
310
return subscribe ((Observer ) o );
291
311
}
292
312
293
- // lookup and memoize onNext
294
313
if (o == null ) {
295
- throw new RuntimeException ("onNext must be implemented " );
314
+ throw new IllegalArgumentException ("onNext can not be null " );
296
315
}
316
+
317
+ // lookup and memoize onNext
297
318
final FuncN onNext = Functions .from (o );
298
319
299
320
/**
@@ -311,7 +332,7 @@ public void onCompleted() {
311
332
@ Override
312
333
public void onError (Exception e ) {
313
334
handleError (e );
314
- // no callback defined
335
+ throw new OnErrorNotImplementedException ( e );
315
336
}
316
337
317
338
@ Override
@@ -327,6 +348,9 @@ public Subscription subscribe(final Object o, Scheduler scheduler) {
327
348
}
328
349
329
350
public Subscription subscribe (final Action1 <T > onNext ) {
351
+ if (onNext == null ) {
352
+ throw new IllegalArgumentException ("onNext can not be null" );
353
+ }
330
354
331
355
/**
332
356
* Wrapping since raw functions provided by the user are being invoked.
@@ -343,14 +367,11 @@ public void onCompleted() {
343
367
@ Override
344
368
public void onError (Exception e ) {
345
369
handleError (e );
346
- // no callback defined
370
+ throw new OnErrorNotImplementedException ( e );
347
371
}
348
372
349
373
@ Override
350
374
public void onNext (T args ) {
351
- if (onNext == null ) {
352
- throw new RuntimeException ("onNext must be implemented" );
353
- }
354
375
onNext .call (args );
355
376
}
356
377
@@ -363,10 +384,14 @@ public Subscription subscribe(final Action1<T> onNext, Scheduler scheduler) {
363
384
364
385
@ SuppressWarnings ({ "rawtypes" , "unchecked" })
365
386
public Subscription subscribe (final Object onNext , final Object onError ) {
366
- // lookup and memoize onNext
367
387
if (onNext == null ) {
368
- throw new RuntimeException ("onNext must be implemented" );
388
+ throw new IllegalArgumentException ("onNext can not be null" );
389
+ }
390
+ if (onError == null ) {
391
+ throw new IllegalArgumentException ("onError can not be null" );
369
392
}
393
+
394
+ // lookup and memoize onNext
370
395
final FuncN onNextFunction = Functions .from (onNext );
371
396
372
397
/**
@@ -384,9 +409,7 @@ public void onCompleted() {
384
409
@ Override
385
410
public void onError (Exception e ) {
386
411
handleError (e );
387
- if (onError != null ) {
388
- Functions .from (onError ).call (e );
389
- }
412
+ Functions .from (onError ).call (e );
390
413
}
391
414
392
415
@ Override
@@ -402,6 +425,12 @@ public Subscription subscribe(final Object onNext, final Object onError, Schedul
402
425
}
403
426
404
427
public Subscription subscribe (final Action1 <T > onNext , final Action1 <Exception > onError ) {
428
+ if (onNext == null ) {
429
+ throw new IllegalArgumentException ("onNext can not be null" );
430
+ }
431
+ if (onError == null ) {
432
+ throw new IllegalArgumentException ("onError can not be null" );
433
+ }
405
434
406
435
/**
407
436
* Wrapping since raw functions provided by the user are being invoked.
@@ -418,16 +447,11 @@ public void onCompleted() {
418
447
@ Override
419
448
public void onError (Exception e ) {
420
449
handleError (e );
421
- if (onError != null ) {
422
- onError .call (e );
423
- }
450
+ onError .call (e );
424
451
}
425
452
426
453
@ Override
427
454
public void onNext (T args ) {
428
- if (onNext == null ) {
429
- throw new RuntimeException ("onNext must be implemented" );
430
- }
431
455
onNext .call (args );
432
456
}
433
457
@@ -440,10 +464,17 @@ public Subscription subscribe(final Action1<T> onNext, final Action1<Exception>
440
464
441
465
@ SuppressWarnings ({ "rawtypes" , "unchecked" })
442
466
public Subscription subscribe (final Object onNext , final Object onError , final Object onComplete ) {
443
- // lookup and memoize onNext
444
467
if (onNext == null ) {
445
- throw new RuntimeException ("onNext must be implemented" );
468
+ throw new IllegalArgumentException ("onNext can not be null" );
469
+ }
470
+ if (onError == null ) {
471
+ throw new IllegalArgumentException ("onError can not be null" );
472
+ }
473
+ if (onComplete == null ) {
474
+ throw new IllegalArgumentException ("onComplete can not be null" );
446
475
}
476
+
477
+ // lookup and memoize onNext
447
478
final FuncN onNextFunction = Functions .from (onNext );
448
479
449
480
/**
@@ -455,17 +486,13 @@ public Subscription subscribe(final Object onNext, final Object onError, final O
455
486
456
487
@ Override
457
488
public void onCompleted () {
458
- if (onComplete != null ) {
459
- Functions .from (onComplete ).call ();
460
- }
489
+ Functions .from (onComplete ).call ();
461
490
}
462
491
463
492
@ Override
464
493
public void onError (Exception e ) {
465
494
handleError (e );
466
- if (onError != null ) {
467
- Functions .from (onError ).call (e );
468
- }
495
+ Functions .from (onError ).call (e );
469
496
}
470
497
471
498
@ Override
@@ -481,6 +508,15 @@ public Subscription subscribe(final Object onNext, final Object onError, final O
481
508
}
482
509
483
510
public Subscription subscribe (final Action1 <T > onNext , final Action1 <Exception > onError , final Action0 onComplete ) {
511
+ if (onNext == null ) {
512
+ throw new IllegalArgumentException ("onNext can not be null" );
513
+ }
514
+ if (onError == null ) {
515
+ throw new IllegalArgumentException ("onError can not be null" );
516
+ }
517
+ if (onComplete == null ) {
518
+ throw new IllegalArgumentException ("onComplete can not be null" );
519
+ }
484
520
485
521
/**
486
522
* Wrapping since raw functions provided by the user are being invoked.
@@ -497,16 +533,11 @@ public void onCompleted() {
497
533
@ Override
498
534
public void onError (Exception e ) {
499
535
handleError (e );
500
- if (onError != null ) {
501
- onError .call (e );
502
- }
536
+ onError .call (e );
503
537
}
504
538
505
539
@ Override
506
540
public void onNext (T args ) {
507
- if (onNext == null ) {
508
- throw new RuntimeException ("onNext must be implemented" );
509
- }
510
541
onNext .call (args );
511
542
}
512
543
@@ -3768,6 +3799,79 @@ public void call(String v) {
3768
3799
assertEquals (1 , counter .get ());
3769
3800
}
3770
3801
3802
+ /**
3803
+ * https://github.com/Netflix/RxJava/issues/198
3804
+ *
3805
+ * Rx Design Guidelines 5.2
3806
+ *
3807
+ * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be
3808
+ * to rethrow the exception on the thread that the message comes out from the observable sequence.
3809
+ * The OnCompleted behavior in this case is to do nothing."
3810
+ */
3811
+ @ Test
3812
+ public void testErrorThrownWithoutErrorHandlerSynchronous () {
3813
+ try {
3814
+ error (new RuntimeException ("failure" )).subscribe (new Action1 <Object >() {
3815
+
3816
+ @ Override
3817
+ public void call (Object t1 ) {
3818
+ // won't get anything
3819
+ }
3820
+
3821
+ });
3822
+ fail ("expected exception" );
3823
+ } catch (Exception e ) {
3824
+ assertEquals ("failure" , e .getMessage ());
3825
+ }
3826
+ }
3827
+
3828
+ /**
3829
+ * https://github.com/Netflix/RxJava/issues/198
3830
+ *
3831
+ * Rx Design Guidelines 5.2
3832
+ *
3833
+ * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be
3834
+ * to rethrow the exception on the thread that the message comes out from the observable sequence.
3835
+ * The OnCompleted behavior in this case is to do nothing."
3836
+ *
3837
+ * @throws InterruptedException
3838
+ */
3839
+ @ Test
3840
+ public void testErrorThrownWithoutErrorHandlerAsynchronous () throws InterruptedException {
3841
+ final CountDownLatch latch = new CountDownLatch (1 );
3842
+ final AtomicReference <Exception > exception = new AtomicReference <Exception >();
3843
+ Observable .create (new Func1 <Observer <String >, Subscription >() {
3844
+
3845
+ @ Override
3846
+ public Subscription call (final Observer <String > observer ) {
3847
+ new Thread (new Runnable () {
3848
+
3849
+ @ Override
3850
+ public void run () {
3851
+ try {
3852
+ observer .onError (new RuntimeException ("failure" ));
3853
+ } catch (Exception e ) {
3854
+ // without an onError handler it has to just throw on whatever thread invokes it
3855
+ exception .set (e );
3856
+ }
3857
+ latch .countDown ();
3858
+ }
3859
+ }).start ();
3860
+ return Subscriptions .empty ();
3861
+ }
3862
+ }).subscribe (new Action1 <Object >() {
3863
+
3864
+ @ Override
3865
+ public void call (Object t1 ) {
3866
+
3867
+ }
3868
+
3869
+ });
3870
+ // wait for exception
3871
+ latch .await (3000 , TimeUnit .MILLISECONDS );
3872
+ assertNotNull (exception .get ());
3873
+ assertEquals ("failure" , exception .get ().getMessage ());
3874
+ }
3771
3875
}
3772
3876
3773
3877
}
0 commit comments