|
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;
|
@@ -189,10 +190,16 @@ public Subscription subscribe(Observer<T> observer) {
|
189 | 190 | subscription.wrap(onSubscribeFunction.call(new AtomicObserver<T>(subscription, observer)));
|
190 | 191 | return hook.onSubscribeReturn(this, subscription);
|
191 | 192 | }
|
| 193 | + } catch (OnErrorNotImplementedException e) { |
| 194 | + // special handling when onError is not implemented ... we just rethrow |
| 195 | + throw e; |
192 | 196 | } catch (Exception e) {
|
193 | 197 | // if an unhandled error occurs executing the onSubscribe we will propagate it
|
194 | 198 | try {
|
195 | 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; |
196 | 203 | } catch (Exception e2) {
|
197 | 204 | // if this happens it means the onError itself failed (perhaps an invalid function implementation)
|
198 | 205 | // so we are unable to propagate the error correctly and will just throw
|
@@ -279,6 +286,8 @@ public void onError(Exception e) {
|
279 | 286 | Object onError = callbacks.get("onError");
|
280 | 287 | if (onError != null) {
|
281 | 288 | Functions.from(onError).call(e);
|
| 289 | + } else { |
| 290 | + throw new OnErrorNotImplementedException(e); |
282 | 291 | }
|
283 | 292 | }
|
284 | 293 |
|
@@ -323,7 +332,7 @@ public void onCompleted() {
|
323 | 332 | @Override
|
324 | 333 | public void onError(Exception e) {
|
325 | 334 | handleError(e);
|
326 |
| - // no callback defined |
| 335 | + throw new OnErrorNotImplementedException(e); |
327 | 336 | }
|
328 | 337 |
|
329 | 338 | @Override
|
@@ -358,7 +367,7 @@ public void onCompleted() {
|
358 | 367 | @Override
|
359 | 368 | public void onError(Exception e) {
|
360 | 369 | handleError(e);
|
361 |
| - // no callback defined |
| 370 | + throw new OnErrorNotImplementedException(e); |
362 | 371 | }
|
363 | 372 |
|
364 | 373 | @Override
|
@@ -3739,6 +3748,79 @@ public void call(String v) {
|
3739 | 3748 | assertEquals(1, counter.get());
|
3740 | 3749 | }
|
3741 | 3750 |
|
| 3751 | + /** |
| 3752 | + * https://github.com/Netflix/RxJava/issues/198 |
| 3753 | + * |
| 3754 | + * Rx Design Guidelines 5.2 |
| 3755 | + * |
| 3756 | + * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be |
| 3757 | + * to rethrow the exception on the thread that the message comes out from the observable sequence. |
| 3758 | + * The OnCompleted behavior in this case is to do nothing." |
| 3759 | + */ |
| 3760 | + @Test |
| 3761 | + public void testErrorThrownWithoutErrorHandlerSynchronous() { |
| 3762 | + try { |
| 3763 | + error(new RuntimeException("failure")).subscribe(new Action1<Object>() { |
| 3764 | + |
| 3765 | + @Override |
| 3766 | + public void call(Object t1) { |
| 3767 | + // won't get anything |
| 3768 | + } |
| 3769 | + |
| 3770 | + }); |
| 3771 | + fail("expected exception"); |
| 3772 | + } catch (Exception e) { |
| 3773 | + assertEquals("failure", e.getMessage()); |
| 3774 | + } |
| 3775 | + } |
| 3776 | + |
| 3777 | + /** |
| 3778 | + * https://github.com/Netflix/RxJava/issues/198 |
| 3779 | + * |
| 3780 | + * Rx Design Guidelines 5.2 |
| 3781 | + * |
| 3782 | + * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be |
| 3783 | + * to rethrow the exception on the thread that the message comes out from the observable sequence. |
| 3784 | + * The OnCompleted behavior in this case is to do nothing." |
| 3785 | + * |
| 3786 | + * @throws InterruptedException |
| 3787 | + */ |
| 3788 | + @Test |
| 3789 | + public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException { |
| 3790 | + final CountDownLatch latch = new CountDownLatch(1); |
| 3791 | + final AtomicReference<Exception> exception = new AtomicReference<Exception>(); |
| 3792 | + Observable.create(new Func1<Observer<String>, Subscription>() { |
| 3793 | + |
| 3794 | + @Override |
| 3795 | + public Subscription call(final Observer<String> observer) { |
| 3796 | + new Thread(new Runnable() { |
| 3797 | + |
| 3798 | + @Override |
| 3799 | + public void run() { |
| 3800 | + try { |
| 3801 | + observer.onError(new RuntimeException("failure")); |
| 3802 | + } catch (Exception e) { |
| 3803 | + // without an onError handler it has to just throw on whatever thread invokes it |
| 3804 | + exception.set(e); |
| 3805 | + } |
| 3806 | + latch.countDown(); |
| 3807 | + } |
| 3808 | + }).start(); |
| 3809 | + return Subscriptions.empty(); |
| 3810 | + } |
| 3811 | + }).subscribe(new Action1<Object>() { |
| 3812 | + |
| 3813 | + @Override |
| 3814 | + public void call(Object t1) { |
| 3815 | + |
| 3816 | + } |
| 3817 | + |
| 3818 | + }); |
| 3819 | + // wait for exception |
| 3820 | + latch.await(3000, TimeUnit.MILLISECONDS); |
| 3821 | + assertNotNull(exception.get()); |
| 3822 | + assertEquals("failure", exception.get().getMessage()); |
| 3823 | + } |
3742 | 3824 | }
|
3743 | 3825 |
|
3744 | 3826 | }
|
0 commit comments