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.
- executeSynchronous() - waits until I have a result, returns the result.
- executeAsynchronous() - 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.
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 {
// does this have to be final?
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 (CancellationException e) {
// what to do here?
} catch (InterruptedException e) {
// is this right way to deal with InterruptedException?
throw new RuntimeException("Interrupted", e);
} catch (ExecutionException e) {
// what do you mean by ExecutionException? And how should we deal with this?
DataLogging.logErrors(e.getCause(), DataErrorEnum.ERROR_CLIENT, keys);
response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
} catch (TimeoutException e) {
DataLogging.logErrors(e.getCause(), DataErrorEnum.TIMEOUT_ON_CLIENT, keys);
response = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_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:
- How do I properly deal with exceptions in the catch block of
executeSync
? Is there any difference between CancellationException and TimeoutException? Also what we should do withExecutionException
in general? - Does my DataKey have to be final in my interface? If I remove final variable in my executeAsync implementation, then I get compilation error as
Cannot refer to a non-final variable keys inside an inner class defined in a different method
. - Is this the right way to use ListenableFutureCallback in my
executeAsync
method? Or is there any better way to use that?
Any inputs/suggestions are also welcome on my design for having sync and async implementations.