Handling CompletableFuture exceptions in controller - java

I'm trying to get into CompletableFuture class for a project I'm running, and I got to some question here:
There is the following method: it tries to find a conversation by its ID or hash; and, if not found, it throws an exception. So far, so good.
public ConversationOutput getConversationByIdOrHash(String conversationIdOrHash)
throws ConversationNotFoundException {
Conversation conversation = this.conversationRepository.getByIdOrHash(conversationIdOrHash);
if (conversation == null) {
throw new ConversationNotFoundException(conversationIdOrHash);
}
return this.modelMapper.map(conversation, ConversationOutput.class);
}
Note that I am throwing ConversationNotFoundException from my method signature. My SpringBoot controller is reacting to this exception and it's all working fine since the beginning.
What I'm trying to do is to make this to a CompletableFuture return and actually throwing an exception, something similar to:
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash)
throws ConversationNotFoundException {
return CompletableFuture.supplyAsync(() -> this.getConversationByIdOrHash(conversationIdOrHash));
}
I've seen posts where people use exceptionally to handle exceptions, but what I really want to do is to throw it to my controller and let it handle it. Any suggestions of how can I make it?
Thank you all!

The question is do you care about the result of CompletableFuture.
CompletableFuture is like a special task and it is processed on other thread. If you don't invoke .join() you won't receive the results of CompletableFuture. This method also will propagate the exception if any occured. However it waits for CompletableFuture to finish and blocks the request.
However, there is no way to get exceptions from the inside of the CompletableFuture without waiting, you have to treat it like other task.

You can pass the completed future in case of a success, and failed future along with your custom exception.
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash) {
try {
return CompletableFuture.completedFuture(this.getConversationByIdOrHash(conversationIdOrHash));
} catch (ConversationNotFoundException e) {
return CompletableFuture.failedFuture(e);
}
}
and then at your controller level you can handle the exception.
final CompletableFuture<ConversationOutput> future = getConversationByIdOrHashAsync("idOrHash");
future.whenComplete((r, e) -> {
if (e != null) {
if (e instanceof ConversationNotFoundException) {
//handling
}
}
});

Related

Is CompletableFuture followed immediately by a get efficient?

I just found the following code, it supplies an asynchronous task but immediately get the result (so if I understand correctly, it blocks the current thread until the result is available).
Is it efficient ?
public String myMethod() {
CompletableFuture<String> futur = CompletableFuture.supplyAsync(() -> {
// my long call to an external API
return "theResult";
});
try {
return future.get(FUTURE_TIMEOUT_DURATION, TimeUnit.MINUTES);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
If the timeout is handled correctly in the call to the external API, do I need this completable futur ?
Is it ok to simplify the code like this ?
public String myMethod() {
// my long call to an external API
return "theResult";
}
If you doesn't expect any problem with timeout you most probably can remove code related to feature.
There is possibility that code use some threadlocal variables or otherwise relies on being executed in separate thread.

Mocking the CompletableFuture.join() for IntegrationTests

Iam writing the Integration test cases and i was stuck at point where i was not able to mock the
CompletableFuture.join()
Firstly,
I will make an async call and add all the responses to list
#Async("AsyncTaskExecutor")
public <T> CompletableFuture<ResponseEntity<T>> callCarrierPost(
ServiceConfig serviceConfig, Class<T> responseType, ExecutionContext executionContext,
AdapterContext adapterContext) {
ResponseEntity<T> responseEntity = carrierInvoker.postForObject(
serviceConfig, responseType, executionContext, adapterContext);
return CompletableFuture.completedFuture(responseEntity);
}
Once the async call is made then i will process the responses of the async calls like below,
private <T> List<ResponseEntity<T>> processResponseFutureList(List<CompletableFuture<ResponseEntity<T>>> responseEntityFutureList) {
List<ResponseEntity<T>> responseEntityList = new ArrayList<>();
responseEntityFutureList.forEach(responseEntityFuture -> {
try {
responseEntityList.add(responseEntityFuture.join());
} catch (CompletionException ex) {
if (ex.getCause() instanceof HttpStatusCodeException) {
HttpStatusCodeException httpStatusCodeException = ((HttpStatusCodeException) ex.getCause());
ResponseEntity<T> response = new ResponseEntity<>((T) httpStatusCodeException.getResponseBodyAsString(),
httpStatusCodeException.getResponseHeaders(),
httpStatusCodeException.getStatusCode());
responseEntityList.add(response);
} else if (ex.getCause() instanceof ResourceAccessException &&
ex.getCause().getCause() instanceof SocketTimeoutException) {
responseEntityList.add(getErrorResponseEntity(HttpStatus.SERVICE_UNAVAILABLE,
TimeOutException.Code.PROVIDER_TIME_OUT.getVal(), ex.getMessage()));
} else {
responseEntityList.add(getErrorResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR,
InternalServerException.Code.INTERNAL_M2_BROKER_ERROR.getVal(), ex.getMessage()));
}
}
});
return responseEntityList;
}
From processResponseFutureList method, Iam trying to mock the response of the completableFuture.join() to cover all the exceptional scenarios
So i tried to mock completableFuture, but no luck, it was not throwing an exception with below changes, instead it gives the original response.
#MockBean
private CompletableFuture completableFuture;
Mockito.when(completableFuture.join())
.thenReturn(new ResourceAccessException("I/O error on /uri", new SocketTimeoutException("Read Timeout")));
Iam actually new to testing and also never got an chance to work with CompletableFuture
Can someone help to mock the CompletableFuture.join() to throw an exception.
In general, don't mock types you don't own. In particular, CompletableFuture is an enormous API with very complicated semantics and I can't recommend mocking it. (Your team's opinion may vary, but CompletableFuture's large and non-encapsulated API is known as a design issue, particularly in how a CompletableFuture can be controlled from outside its source.)
Furthermore, join will never return a ResourceAccessException, nor throw one directly. Futures represent the result of some other asynchronous process, probably on another thread; if that process throws a ResourceAccessException, then as in the code you posted, join will throw a CompletionException with a getCause() value that is the underlying ResourceAccessException.
In Java 9 or better, you can use failedFuture as a static factory, passing in the raw ResourceAccessException because the real CompletableFuture implementation will wrap it for you:
// You'll probably need to specify your generics here, but I can't see
// enough of your test to fill them in realistically.
CompletableFuture</* ... */> completableFuture
= CompletableFuture.failedFuture(new ResourceAccessException(
"I/O error on /uri",
new SocketTimeoutException("Read Timeout")));
In Java 8, in absence of a static factory as in the SO question "CompletableFuture already completed with an exception", just create a real CompletableFuture and complete it exceptionally (taking advantage of the aforementioned external-control design issue):
CompletableFuture</* ... */> completableFuture
= new CompletableFuture</* ... */>();
completableFuture.completeExceptionally(
new ResourceAccessException(/*...*/));

How to mock completablefuture.get()?

This is the method I'm trying to mock:
#VisibleForTesting
public List<Row> processRows2(CompletableFuture future) {
List<Row> rows2 = new ArrayList<>();
try {
DefaultAsyncResultSet beep = (DefaultAsyncResultSet) future.get();
for (Row b : beep.currentPage()) {
rows2.add(b);
}
}
catch (ExecutionException | InterruptedException e) {
LOGGER.error(e);
LOGGER.error(e.getStackTrace());
throw new RuntimeException(e.getMessage() + " - Check thread pool resources are enough, may be too many queries in queue");
}
return rows2;
}
The problem is that when I try to test it with this (currently just trying to get it to run all the way to either success or failure):
#Test
public void processRows2test() {
FeatureDaoImpl gar = new FeatureDaoImpl(connection);
CompletableFuture L = new CompletableFuture();
gar.processRows2(L);
}
It hangs endlessly. My guess is that the future.get() is where it's hanging; I'm not sure. But I'm not sure how to mock that. I've tried this:
#Mock
private CompletableFuture mockFutures;
#Before
public void setUp() {
try {
Mockito.when(mockFutures.get()).thenReturn((AsyncResultSet) mockResultSetFuture);
}
catch (Exception e) {
}
}
But this I feel is not correct. The try catch is because it yells at me about unhandled exceptions on the get(), so I don't know how to get around that.
I have also now tried this:
#Mock
final CompletableFuture<List<String>> mockedFuture = Mockito.mock(CompletableFuture.class);
With the following in the setup:
Mockito.doReturn(new ArrayList<Row>()).when(mockedFuture).get();
But it still hangs endlessly.
I've seen these:
How to mock completion of a CompletableFuture in Mockito
This one I don't understand what exactly it's trying to get me to do, and doesn't feel super applicable, because it's not a get method. I saw some examples here that have .get() in them... but none were mocked methods unfortunately, they were gets in the test itself: https://www.javatips.net/api/java.util.concurrent.completablefuture
EDIT: the code runs. It returns results. So it isn't that the actual method isn't returning a value - I know it does this, it's doing it in QA right now.
Your current CompletableFuture is not completed, so the .get() method hangs waiting for async completion that will never happen. You can use CompletableFuture.completedFuture(value) to create a CompletableFuture instance that will return the passed value when .get() is called on it.
You can use the CompletableFuture.completedFuture method here
#Test
public void processRows2test() {
FeatureDaoImpl gar = new FeatureDaoImpl(connection);
CompletableFuture L = CompletableFuture.completedFuture(new ArrayList<Row>());
gar.processRows2(L);
}

Break Completable Future's then apply chain

I have many calls that look like these. The problem is that next call fully depends on previous one. If there isn't any conversations fetching messages from them makes no sense so i just want break this chain. I read few topics with Holger's answers but I feel I still don't fully understand this. Could someone give me some examples based on this code?
public CompletableFuture<Set<Conversation>> fetchConversations(List<Information> data, String sessionId)
{
return myservice
.get(prepareRequest(data, sessionId))
.thenApply(HtmlResponse::getDocument)
.thenApply(this::extractConversationsFromDocument);
}
public CompletableFuture<Elements> fetchMessagesFromConversation(String Url, String sessionId)
{
return mySerice
.get(prepareRequest(url, sessionId))
.thenApply(HtmlResponse::getDocument)
.thenApply(this::extractMessageFromConversation);
}
Throwing an exception from any of your chain steps will skip all subsequent steps: none of the thenApply() callbacks will be called and the future will be resolved with the exception occurred. You can use it to break your chain. For example, consider the following code:
public CompletableFuture<Set<Conversation>> fetchConversations(List<Information> data, String sessionId) {
return myservice
.get(prepareRequest(data, sessionId))
.thenApply(HtmlResponse::getDocument)
.thenApply(value -> {
if (checkSomeCondition(value))
throw new CompletionException(new CustomException("Reason"));
return value;
})
.thenApply(this::extractConversationsFromDocument)
.exceptionally(e -> {
// the .thenApply(this::extractConversationsFromDocument) step
// was not executed
return Collections.emptySet(); //or null
});
}
You can add a step in which you check the value returned from the previous step, and, depending on some condition, throw an exception.
Then after the last .thenApply you can add an exceptionally handler and return an empty Set, null or something else as an unsuccessful result.
You can also omit the exceptionally handler. In this case you have to catch the exception at the end of the chain, where you finally call .get():
try {
Set<Conversation> conversations = fetchConversations(data, id).get();
} catch (InterruptedException e) {
// handle the InterruptedException
e.printStackTrace();
} catch (ExecutionException e) {
// handle the ExecutionException
// e.getCause() is your CustomException or any other exception thrown from the chain
}

How to signify failure of a Java Future result

AFAIK submitting Callable/Runnable to ExecutorService is the way to go if I want to execute resource-heavy code in parallel. Hence my method structure:
public class ServiceClass {
protected final ExecutorService executorService = Executors.newCachedThreadPool();
public Future<Result> getResult(Object params) {
if (params == null) {
return null; // In situations like this the method should fail
}
// Do other fast pre-processing stuff
return executorService.submit(new CallProcessResult(params));
}
private class CallProcessResult implements Callable<Result> {
private Object params;
public CallProcessResult(Object params) {
this.params = params;
}
#Override
public Result call() throws Exception {
// Compute result for given params
// Failure may happen here too!
return result;
}
}
}
public class Result {
...
}
I have marked 2 spots in the code above in which failures can happen. The options available for error handling are quite different for those 2 cases.
Before submitting the task there can be issues like invalid parameters, some fast pre-processing code that may fail.
I see several ways to signify failure here:
In case of invalid params supplied to getResult return null immediately. In this case I'll have to check if getResult returned null every time I call it.
Throw checked exceptions instead of the above.
Instantiate a Future<Result> that returns null on get() request. I would do that with Apache Commons ConcurrentUtils.constantFuture(null). In this case I would expect getResult to always return some non-null Future<Result>. I like this option more, because it is consistent with the second case.
During task execution I can expect serious errors like lack of memory, corrupted files, unavailable files etc.
I suppose the better option in my case is to return null, because the result of the task is an object.
Also, I could throw checked exceptions and handle them in ThreadPoolExecutor.afterExecute (as suggested by NiranjanBhat). See Handling exceptions from Java ExecutorService tasks
Which is the better practice (in both cases)?
Perhaps there is a different way to do this or a design pattern I should use?
I would suggest that for failure during task processing, you simply throw an appropriate exception. Don't add any special handling for this in the executor. What will happen is that it will be captured, and stored in the Future. When the Future's get method is called, it will throw an ExecutionException, which the caller of get can then unpack and handle. This is essentially how normal exception handling is transposed into the Callable/Future paradigm. This looks like this:
Future<Result> futureResult = serviceClass.getResult("foo");
try {
Result result = futureResult.get();
// do something with result
}
catch (ExecutionException ee) {
Throwable e = ee.getCause();
// do something with e
}
Given that the caller of get has to have this handling of ExecutionExceptions, you could then take advantage of that to deal with failure during submission. To do this, you could construct a Future that is like Apache Commons's constantFuture, but which throws a given exception rather than returns a given value. I don't think there's anything like that in the JDK, but it's simple (if tedious) to write:
public class FailedFuture<T> implements Future<T> {
private final Throwable exception;
public FailedFuture(Throwable exception) {
this.exception = exception;
}
#Override
public T get() throws ExecutionException {
throw new ExecutionException(exception);
}
#Override
public T get(long timeout, TimeUnit unit) throws ExecutionException {
return get();
}
#Override public boolean cancel(boolean mayInterruptIfRunning) { return false; }
#Override public boolean isCancelled() { return false; }
#Override public boolean isDone() { return true; }
}
This is somewhat dodgy - you're taking a failure during a synchronously-called method, and making it look like a failure during the asynchronously-called method. You're shifting the burden of handling the error from the code that actually caused it to some code that runs later. Still, it does mean you can have all the failure handling code in one place; that might be enough of an advantage to make this worthwhile.
You can use afterExecute method. This is defined in the ThreadPoolExecutor, which you will need to override.
This method is called after the execution of each task is completed. You will get the task instance in this callback method. You can record the errors in some variable in your task and access it in this method.

Categories