Retry once using functional interface

563 views Asked by At

I'm new to functional programming (and Java) and I want to create something nice using it, but cannot figure out how.

I'm using BiConsumer (java.util.function.BiConsumer) for calls to the server.

I'm trying to write a code which send some request to server with existing data (calling Public createSomeTask()), only if it fails, the connector will perform a retry only once.

The code is async, Private createSomeTask() calls updateTaskStage() as well.

The problem is that I have many calls in the connector (many createSomeTask()) with current implementation, the code not looks good and I'm sure there is a way to do it better using Functional interfaces or extending the BiConsumer or any other nice way in Java 8 lile , reflection, method invoke etc.

See the code below. I hope someone can help me with that.

//Main
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Connector connector = new Connector();
        connector.createSomeTask("started",(response, error)->{
        });
    }
}

//Connector Class
import java.util.function.BiConsumer;

public class Connector {
    static String taskStage = null;

    public void createSomeTask(final String taskName,
                               final BiConsumer<ServerResponse, ServerError> consumer) {
        createSomeTask(taskName, true, consumer);
    }

    private void createSomeTask(final String taskName,
                                boolean retry,
                                final BiConsumer<ServerResponse, ServerError> consumer) {
        updateTaskStage((newStage, error)->{
            if(error!=null) {
                if (retry) {
                    createSomeTask(taskName, false, consumer); //The 'recursive' retry call
                    return;
                } else {
                    consumer.accept(null, error);//only after second failure
                    return;
                }
            }
            consumer.accept(new ServerResponse(),null);
        });

    }

    //re-uses existing task or starting a new one when the current is null / failed
    private void updateTaskStage(final BiConsumer<String, ServerError> consumer) {
        if(taskStage ==null || taskStage != "failed")
            consumer.accept(taskStage,null);
        else
        {
            taskStage = "started";
            consumer.accept(taskStage,null);
        }
    }

    class ServerResponse{}
    class ServerError{}
}
1

There are 1 answers

0
Holger On

There is no reason to check the retry flag in the consumer, as you can compose a function doing the right thing before passing it to updateTaskStage:

private void createSomeTask(
    String taskName, boolean retry, BiConsumer<ServerResponse, ServerError> consumer) {

    BiConsumer<String, ServerError> direct
        = (s, e) -> consumer.accept(e==null? new ServerResponse(): null, e);

    BiConsumer<String, ServerError> actual = retry?
        (string, error) -> {
            if(error!=null) updateTaskStage(direct); else direct.accept(string, null);
        }:
        direct;

    updateTaskStage(actual);
}

First, a function is created passing a translation of the back-end’s response to the provided consumer.

Then, only if retry is true, the function is composed with another function which will do a second try in the error case. For the second attempt, the direct function can be passed to updateTaskStage, removing the need for recursion.

As it seems that the retry parameter wasn’t supposed to be an option, but only part of your attempt of solving the problem, you can now get rid of it, when always performing one retry is intended:

public void createSomeTask(
    String taskName, BiConsumer<ServerResponse, ServerError> consumer) {

    BiConsumer<String, ServerError> direct
        = (s, e) -> consumer.accept(e==null? new ServerResponse(): null, e);

    BiConsumer<String, ServerError> actual=
        (string, error) -> {
            if(error!=null) updateTaskStage(direct); else direct.accept(string, null);
        };

    updateTaskStage(actual);
}