How to properly deal with exceptions coming from ListenableFuture guava?

a 夏天 提交于 2019-12-01 23:14:22

I assume you're using Spring 4 (AsyncRestTemplate). In this case ListenableFuture that you get is not really Guava's ListenableFuture, but it's clone in Spring. Anyway you should deal with exceptions the same way as you deal with exceptions from the standard Future.

Answers to your questions:

// does this have to be final? private final AsyncRestTemplate
restTemplate = new AsyncRestTemplate();

It doesn't (in this case), but this is a good practice as in general it makes object less mutable simplifying reasoning about it's behaviour.

catch (CancellationException e) {
    // what to do here?
}

CancellationException will be thrown if task is cancelled (either via Future#cancel or ExecutorService#shutdownNow). It cannot happen in your case as only you have references to Future and (implicitly via private AsyncRestTemplate) ExecutorService used by execute queries. So

throw new AssertionError("executeAsync task couldn't be cancelled", e);

Is there any difference between CancellationException and TimeoutException?

In Future#get call you've specified timeout. TimeoutException will be thrown if result is still not available after keys.getTimeout() milliseconds.

catch (InterruptedException e) {
   // is this right way to deal with InterruptedException?
   throw new RuntimeException("Interrupted", e);
}

In this case no. InterruptedException will be thrown when client's thread is interrupted. You don't own that thread so you should propagate InterruptedException (i.e. declare executeSync(DataKey keys) throws InterruptedException). If for some reason you cannot change signature of the method then at least restore interrupted flag (Thread.currentThread().interrupt()) before throwing RuntimeException.

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);
}

ExecutionException means that code submitted to ExecutorService as Callable/Runnable threw exception during execution. In your case ExecutionException will never be thrown because you return SettableFuture with value set in both onSuccess and onFailure callbacks, so you can throw AssertionError in the catch block. There is no general strategy of response to ExecutionException.

Does my DataKey have to be final in my interface?

It must be final in executeAsync implementation because you reference it from anonymous class (onFailure callback);

Is this the right way to use ListenableFutureCallback in my executeAsync method? Or is there any better way to use that?

Don't see anything wrong with it.

Some advices:

  1. Consider making thread pool for async client configurable.

By default AsyncRestTemplate uses SimpleAsyncTaskExecutor which creates new thread for each request. This may be not suitable for all your clients. Note that if you follow this advice response to CancellationException must be different as client now can have reference to ExecutorService: throwing RuntimeException should be fine.

  1. Describe in (java)doc thread pool used by default!

  2. I would split sync and async versions.

  3. I think that using sync RestTemplate and implementing async version by means of sync version would simplify implementation.

  4. Consider returning more flexible ListenableFuture instead of plain Future (using SettableListenableFuture instead of SettableFuture).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!