I have a library in which I have provided two methods, sync and async for our customer. They can call whichever method they feel is right for their purpose.
- executeSync() - waits until I have a result, returns the result.
- executeAsync() - returns a Future immediately which can be processed after other things are done, if needed.
They will pass DataKey object which has the user id in it. And we will figure out which machine to call basis on the user id. So we will make http call to the url using AsyncRestTemplate and then send the response back to them basis on whether it is successful or not.
AsyncRestTemplate (
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/AsyncRestTemplate.html#exchange-java.net.URI-org.springframework.http.HttpMethod-org.springframework.http.HttpEntity-java.lang.Class-) returns back ListenableFuture.
Below is my interface:
public interface Client {
// for synchronous
public DataResponse executeSync(final DataKey key);
// for asynchronous
public Future<DataResponse> executeAsync(final DataKey key);
}
And below is my implementation:
public class DataClient implements IClient {
private final AsyncRestTemplate restTemplate = new AsyncRestTemplate();
@Override
public DataResponse executeSync(final DataKey keys) {
Future<DataResponse> responseFuture = executeAsync(keys);
DataResponse response = null;
try {
response = responseFuture.get(keys.getTimeout(), TimeUnit.Milliseconds);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted", e);
} catch (TimeoutException e) {
DataLogging.logErrors(e.getCause(), DataErrorEnum.TIMEOUT_ON_CLIENT, keys);
response = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
} catch (TimeoutException e) {
DataLogging.logErrors(e.getCause(), DataErrorEnum.ERROR_CLIENT, keys);
response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
}
return response;
}
@Override
public Future<DataResponse> executeAsync(final DataKey keys) {
final SettableFuture<DataResponse> responseFuture = SettableFuture.create();
restTemplate.exchange(createURL(keys), HttpMethod.GET, keys.getEntity(), String.class).addCallback(
new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onSuccess(ResponseEntity<String> result) {
responseFuture.set(new DataResponse(result.getBody(), DataErrorEnum.OK,
DataStatusEnum.SUCCESS));
}
@Override
public void onFailure(Throwable ex) {
DataLogging.logErrors(ex, DataErrorEnum.ERROR_SERVER, keys);
responseFuture.set(new DataResponse(null, DataErrorEnum.ERROR_CLIENT,
DataStatusEnum.ERROR));
}
});
return responseFuture;
}
}
Now my question is: Is this the right implementation of having sync and async implementation in a library which is using Guava ListenableFuture? Or is there any better way to do this? I wanted to have async non blocking architecture so that's why I went with AsyncRestTemplate which returns back to me as ListenableFuture. I have tried to make the example pretty simple, all the logic of figuring out which machine to call basis on user id is not show but what I have will give you the design flow how I am using it.
I don't wanted to implement sync call as async + waiting along with ExecutorService thread pool since it might be a bad idea because it will consume one thread from the thread pool per a call.
Any inputs/suggestions are also welcome on my design for having sync and async implementations in a library which uses Guava ListenableFuture.