1

I need to make a library in which I will have synchronous and asynchronous methods in it.

Core Logic of my Library -

The customer will use our library and they will call it by passing DataKey builder object. We will then construct a URL by using that DataKey object and make a HTTP client call to that URL by executing it and after we get the response back as a JSON String, we will send that JSON String back to our customer as it is by creating DataResponse object.

I will have synchronous and asynchronous methods. Some customer will call the executeSynchronous method to get the same feature and some customer will call our executeAsynchronous method and with the executeAsynchronous method, they will call future.get in there code itself.

Below is my interface -

public interface Client {

    // for synchronous
    public DataResponse executeSynchronous(DataKey dataKey);

    // for asynchronous
    public Future<DataResponse> executeAsynchronous(DataKey dataKey);
}

Below is my DataResponse class -

public class DataResponse {

    private String response;
    private DataErrorEnum error;
    private DataStatusEnum status;

    // constructor here

    // and getters here
}

Below is my DataStatusEnum class -

public enum DataStatusEnum {
    SUCCESS, ERROR;
}

Below is my DataErrorEnum class -

public enum DataErrorEnum {
    NONE(200, "NONE", "Response is success."),
    SERVER_DOWN(3145, "Server Down", "some long message here which can give more details."),
    CLIENT_ERROR(3123, "Client Error", "some long message here which can give more details."),
    TIMEOUT_ON_CLIENT(3187, "Client Timeout", "some long message here which can give more details.");

    private final int code;
    private final String status;
    private final String description;

    // constructor and getters here
}

And then I have my DataClient which implements the above Client interface.

public class DataClient implements Client {

    private RestTemplate restTemplate = new RestTemplate();
    private ExecutorService service = Executors.newFixedThreadPool(10);

    // for synchronous call
    @Override
    public DataResponse executeSynchronous(DataKey dataKey) {
        DataResponse dataResponse = null;

        try {
            Future<String> future = executeAsynchronous(dataKey);
            dataResponse = future.get(dataKey.getTimeout(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey);
            dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    //for asynchronous call
    @Override
    public Future<DataResponse> executeAsynchronous(DataKey dataKey) {
        Future<DataResponse> future = null;

        try {
            Task task = new Task(dataKey, restTemplate);
            future = executor.submit(task);
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey);
        }

        return future;
    }
}

Now below is my simple class which will perform the actual task -

public class Task implements Callable<DataResponse> {

    private DataKey dataKey;
    private RestTemplate restTemplate;

    public Task(DataKey dataKey, RestTemplate restTemplate) {
        this.dataKey = dataKey;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() throws Exception {
        DataResponse dataResponse = null;
        String response = null;

        try {
            String url = createURL();
            response = restTemplate.getForObject(url, String.class);

            // it is a successful response
            dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
        } catch (RestClientException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, dataKey);
            dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    // create a URL by using dataKey object
    private String createURL() {
        String url = somecode;

        return url;
    }
}

Problem Statement:-

As I mentioned above, some customers will call executeSynchronous method to get the data for that user id which they are passing in DataKey object and some customers will call executeAsynchronous method with DataKey object but in latter case, they will do future.get in their code base.

If you see my executeSynchronous method, I am doing future.get after calling executeAsynchronous method and if there is any TimeoutException, then I am logging using PotoLogging class which is specific in our company and that logs will go to some other service here which we use to look all our error logs on the dashboard. And it mainly depends how we are logging it with what names so that we can see those names in the dashboard.

Now the problem is customer within our company can also call executeAsynchronous method but that means, they will do future.get in their code base and that can also result in TimeoutException in their code but I cannot force them to log the same way as I am doing it. So my question is - Is there any way that I can get callback if there is any TimeoutException so that I can log it like this if anyone is calling executeAsynchronous method of my library in their code base -

PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey);

I need to do this so that my library can log TimeoutException in the tool we have in our company in the way we want it. Otherwsie, I need to tell each of the customer to do log it like this so that we can see it in our dashboard. How can I get the callback from asynchronous call and still leverage all the feature of being asynchronous?

What is the best way to do this?

john
  • 11,311
  • 40
  • 131
  • 251

1 Answers1

2

Future is just an interface. Provide an implementation that wraps the instance returned by your service. Make it delegate all its calls to the actual Future and wrap these calls with appropriate try-catch blocks.

Future<DataResponse> wrapper = new Future<DataResponse>() {
    private final Future<DataResponse> delegate = future;

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return delegate.cancel(mayInterruptIfRunning);
    }

    @Override
    public boolean isCancelled() {
        return delegate.isCancelled();
    }

    @Override
    public boolean isDone() {
        return delegate.isDone();
    }

    @Override
    public DataResponse get() throws InterruptedException, ExecutionException {
        DataResponse dataResponse = null;
        try {
            delegate.get();
        } catch (TimeoutException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey);
            dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
        }
        return dataResponse;
    }

    @Override
    public DataResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        DataResponse dataResponse = null;
        try {
            delegate.get(timeout, unit);
        } catch (TimeoutException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey);
            dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
        }
        return dataResponse;
    }
};
return wrapper;
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Thanks for your suggestion. I am not able to understand how will I use this with my current setup or do I need to change anything in my current design? I have never worked with callback and future implementation a lot so having hard time to understand this. If possible can you help me understand how will I use this? – john Mar 12 '15 at 23:23
  • @david You do this in your `executeAsynchronous`, taking the `Future` returned by the `ExecutorService`. You wrap that instance with the `wrapper` I've provided above and return this `wrapper` instance. Notice how the anonymous class in my answer has a `delegate` field which gets initialized with your `future`. – Sotirios Delimanolis Mar 12 '15 at 23:27