javaauthenticationrx-java2retrywhen

RxJava retryWhen retry the whole method that returns a completable


I'm trying to revalidate a token every time the API responds with invalid token error (Re-authentication). I have this small example which replicates the problem i'm facing. Basically, the first call will throw an exception, which will trigger a retry, when it retries, the auth method is not fully called again (it does not print "Entered Auth", but prints "authing...").

public class Example {

AtomicInteger atom = new AtomicInteger(1);

public Example(){}

public void start(){
    auth().andThen(call())
            .retryWhen(throwableFlowable -> throwableFlowable.flatMap(throwable -> {
                System.out.println("Retrying...\n");
                return Flowable.timer(1, TimeUnit.SECONDS);
            }))
            .subscribe(integer -> System.out.println("Result: " + integer), e -> System.out.println("Error" + e.getMessage()));
}

public Completable auth(){
    System.out.println("Entered Auth");
    return Completable.create(emitter -> {
        System.out.println("authing...");
        emitter.onComplete();
    });
}

public Single<String> call(){
    return getId()
            .flatMap(this::getNameById);
}

public Single<Integer> getId(){
    return Single.create(emitter -> {
        emitter.onSuccess(atom.getAndIncrement());
    });
}

public Single<String> getNameById(int id){
    return Single.create(emitter -> {
        HashMap<Integer, String> hash = new HashMap<>();
        hash.put(1, "s");
        hash.put(2, "b");
        if(id == 1){
            emitter.onError(new Throwable());
        }else{
            emitter.onSuccess(hash.get(id));
        }
    });
}

}

Again, here's my output:

Entered Auth
authing...
Retrying...

authing...
Result: b

How can i force the entire auth() method to run on retry?


Solution

  • Use Completable.defer, it will wrap your Completable creation and redo it on retry instead of just resubscribing.

     Completable.defer(() -> auth()).andThen(call())
                    .retryWhen(throwableFlowable -> throwableFlowable.flatMap(throwable -> {
                        System.out.println("Retrying...\n");
                        return Flowable.timer(1, TimeUnit.SECONDS);
                    }))
                    .subscribe(integer -> System.out.println("Result: " + integer), e -> System.out.println("Error" + e.getMessage()));
    

    Output:

    Entered Auth
    authing...
    Retrying...
    
    Entered Auth
    authing...
    Result: b