2

I have code that invokes 2 apis and merges the results. using retrofit and rxJava.

How can I handle it so that if there is a problem with one of the api's i can still get the result from one of the api's that did work?

 IPlaces api = adapter.create(IPlaces.class); //endpoint1
     IPlaces api2 = adapter2.create(IPlaces.class); //endpoint2

    Observable.combineLatest(
            api.getPlacesFrom1("key", placeId),
            api2.getPlacesFrom2(placeId),
        new Func2<PlaceDetailResult1, PlaceDetailResult2, MergedReviews>() {
            @Override
            public MergedReviews call(PlaceDetailResult placeDetailResult1, PlaceDetailResult2 placeDetailResult2) {
                // processToMerge(  placeDetailResult1, placeDetailResult2)
                return mr;
            }

            })
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<MergedReviews>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onNext(MergedReviews mr) {

                    SetAdapter(mr.reviews);
                    SetPhotosAdapter(mr.photos);
                }
            });
raklos
  • 28,027
  • 60
  • 183
  • 301
  • What do you mean by "if there is a problem"? Is there an onError being called, is the `placeDetailResult` invalid, or is there simply no message coming in? – Peter Barmettler Apr 16 '16 at 18:21
  • @PeterBarmettler If one of the Apis is down, id still like the result from the other api to be used.. It does enter onerror (when i a turn off one api), but I would still like the result from the working api to be used, but i just get nothing – raklos Apr 16 '16 at 19:16
  • 1
    I guess the problem is that if you combine two observables and one of them calls onError, the combined observable will also receive onError. The answer to your question depends on how much error handling you want. If you do not need to get noticed about errors you can simply use `.onErrorResumeNext(e->Observable.just(null))` and then handle the `null` cases in the closure of `combineLatest`. Otherwise `mergeDelayError` could be useful. See also this [post](http://stackoverflow.com/questions/22340744/best-practice-for-handling-onerror-and-continuing-processing). – Peter Barmettler Apr 17 '16 at 08:05
  • @PeterBarmettler, think this is along the right lines but not sure how to implement it... In my code if i can somehow get the api that is off to provide null (eg placeDetailResult2 = null) then i can work with that – raklos Apr 17 '16 at 09:26
  • One more question: do your `getPlacesFrom` methods just generate a single result (or an error) or a continuous stream of results? – Peter Barmettler Apr 18 '16 at 18:49
  • each getPlacesFrom generates a single result (of different types) then the processToMerge combines them into a MergedReviews result – raklos Apr 18 '16 at 20:52

1 Answers1

2

As outlined in the comments, you can transform exceptions into null objects (or any other object) using onError*.

Here is a minimal example which I believe captures your setting. It takes two integer observables which provide one Integer value and combines them in some way. If one of the observables yields an error the other value is transmitted, if both yield an error a RuntimeException is thrown. I use zipWith instead of combineLatest since this is sufficient for your case where exactly one value is expected from each of the two observables.

Observable<Integer> zipThrowing(Observable<Integer> observable1, Observable<Integer> observable2) {
        return observable1.onErrorReturn(ex-> null)
                .zipWith(observable2.onErrorReturn(ex->null),
                        (a,b)->{
                            if(b == null) {
                                if(a==null){
                                    throw new RuntimeException("None of the two values are valid.");
                                }
                                return a;
                            }
                            if(a==null) {
                                return b;
                            }
                            return a+b;
                        }
                );

Transforming exceptions into null values may be unacceptable in some cases. If you need the information in your exception I suggest you use a result object containing either the actual value or the exception.

Peter Barmettler
  • 389
  • 2
  • 10
  • does ZipThrowing/ZipWith expect the the arguments to be of the same type? because in my case they are 2 different types which are processed to output a 3rd type – raklos Apr 19 '16 at 17:23
  • The documentation declares: `public final Observable zipWith(Observable extends T2> other, Func2 super T,? super T2,? extends R> zipFunction)`, so, yes you can combine two arguments of different type and you can return an arbitrary type `R`. – Peter Barmettler Apr 19 '16 at 17:57