0

I have a following class:

public class SessionStore {
    Subject<Session, Session> subject;

    public SessionStore() {
       subject = new SerializedSubject<>(BehaviorSubject.create(new Session());
    }

    public void set(Session session) {
        subject.onNext(session);
    }

    public Observable<UserSession> observe() {
        return subject.distinctUntilChanged();
    }
}

In activity I observe the session and perform network operation on each change:

private Subscription init() {
    return sessionStore
            .observe()
            .flatMap(new Func1<Session, Observable<Object>>() {
                @Override
                public Observable<Object> call(Session session) {
                    return (session.isValid() 
                       ? retrofitService.getThingForValid()
                       : retrofitService.getThingForInalid())
                       .subscribeOn(Schedulers.io());
                }
            })
            .subscribe(...);
}

Now I have an Okhttp request interceptor, in which I set the session instance from valid to invalid when network response is non 200 code.

This is what happens:

  1. On initial subscription to session store the getThingForValid() is executed, and fails.
  2. OkHttp intercepts the fail and sets new session.
  3. Session store emits a new, now invalid session.
  4. The new emission executes a getThingForInvalid() method.

What is important to know is that this execution happens in the middle of the previous Retrofit call. This is because OkHttp client is wrapped by Retrofit and all interceptors are executed before Retrofit returns.

Having this in mind, you realize that the second call is being executed and processed by Retrofit already, while the first one hasn't finished yet.

  1. As the first call finishes, it throws HttpException because response was non 200 code.
  2. The xception kills the rx stream and with it the second call.

I have tried to ignore this exception in stream but the second call is cancelled by Retrofit anyways.

Do you have any ideas how to make my concept work please?

bakua
  • 13,704
  • 7
  • 43
  • 62
  • Have you tried something in the vein of `.onErrorResumeNext(err -> Observable.empty())` ? – Tassos Bassoukos Nov 23 '16 at 12:50
  • Yes, It has the very same output. It has to have something to do with Retrofit internals rather than with RxJava. – bakua Nov 23 '16 at 13:17
  • Can you explain abstractly, what you are trying to achieve and what your original problem is? – Sergej Isbrecht Nov 23 '16 at 14:03
  • I have a screen that fetches data from different endpoints depending on whether the user is logged in. To determine if the user is logged in I have a `UserSession` with `isAnonymous()`. I can observe the session as above. When the screen starts it might happen that the access token the app has is not valid any more and the api call will fail. In case the token cannot be renewed, the UserSession is set to be anonymous and Rx stream that fetches data and processes it should be re-executed. – bakua Nov 23 '16 at 14:12
  • What is the response code, when token expired, `401` ? – borichellow Nov 23 '16 at 16:20
  • Yes, the api respects common behaviour. – bakua Nov 23 '16 at 17:44

1 Answers1

0

if you get response code 401 in case of token expiration: you need to add Authenticator in to OkHttpClient.Builder

builder.authenticator(new Authenticator() {
            @Override
            public Request authenticate(Route route, Response response) throws IOException {
                final LoginResponse newLoginResponse = refreshTokenClient.refreshToken();
                //save new token locally, if needed
                return response
                        .request()
                        .newBuilder()
                        .removeHeader("Api-Auth-Token") // removing old header
                        .addHeader("Api-Auth-Token", newLoginResponse.getAuthToken())
                        .build();
            }
        });

where

public interface RefreshTokenService {

   @PUT("/api/v1/tokens")
   LoginResponse refreshToken();

 }

But pay attention: this Authenticator will run each time when response code is 401.

borichellow
  • 1,003
  • 11
  • 11
  • Yes, but I am not able to refresh the token I need to re-execute the bussiness logic I do in stream. Anyways I've resolved my issue with usage of another flat map. I'll post solution later. Thanks :) – bakua Nov 24 '16 at 06:06