Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to throw a custom exception from CompletableFuture?

Question: how can I directly throw a custom exception from .exceptionally()?

List<CompletableFuture<Object>> futures =
    tasks.stream()
        .map(task -> CompletableFuture.supplyAsync(() -> businessLogic(task))
        .exceptionally(ex -> {
                if (ex instanceof BusinessException) return null;

                //TODO how to throw a custom exception here??
                throw new BadRequestException("at least one async task had an exception");
        }))
        .collect(Collectors.toList());

try {
    List<Object> results = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
} catch (CompletionException e) {
        if (e.getCause() instanceof RuntimeException) {
              throw (RuntimeException) e.getCause();
        }
        throw new RuntimeException(e.getCause());
}

Problem: I just always get a CompletionException whose ex.getCause() is instanceof BadRequestException.

Is that possible at all?

like image 999
membersound Avatar asked Feb 28 '18 13:02

membersound


People also ask

How to determine if a completablefuture is thrown with an exception?

In this case no exception is thrown by Java unless we call get () or join () methods. On calling these methods CompletionException is thrown which wraps the actual exception as the root cause exception. Also we can use CompletableFuture.isCompletedExceptionally () method to determine if a CompletableFuture completed with an exception.

How to handle exceptions in Java 8+ completablefuture?

You can use the following methods to handle exceptions in Java 8+ CompletableFuture The stage created by exceptionally / exceptionallyAsync and whenComplete / whenCompleteAsync can only be executed when there're exceptions in the previous stages, while by handle / handleAsync, it can be executed in both normal and exception cases

What is the difference between exceptionally() and completionexception?

Simply if there's no exception then exceptionally () stage is skipped otherwise it is executed. The exception passed to the exceptionally function is CompletionException which wraps the actual exception as its root cause. exceptionally () stage can be used to provide a replacement result.

What happens when we re-throw the cause of a completionexception?

When we re-throw the cause of the CompletionException, we may face unchecked exceptions, i.e. subclasses of Error or RuntimeException, or our custom checked exception ServerException. The code above handles all of them with a multi-catch which will re-throw them.


Video Answer


2 Answers

As said by Didier L, exceptions thrown by the functions (or generally exceptions that completed a CompletableFuture) are always wrapped in a CompletionException (unless they are already a CompletionException or CancellationException).

But note that your code becomes much simpler when not even trying to translate the exception via exceptionally:

List<CompletableFuture<Object>> futures =
    tasks.stream()
        .map(task -> CompletableFuture.supplyAsync(() -> businessLogic(task)))
        .collect(Collectors.toList());
try {
    List<Object> results = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
} catch (CompletionException e) {
    throw e.getCause() instanceof BusinessException?
        new BadRequestException("at least one async task had an exception"): e;
}

or

… catch (CompletionException e) {
    throw e.getCause() instanceof BusinessException?
        new BadRequestException("at least one async task had an exception"):
        e.getCause() instanceof BusinessException? (RuntimeException)e.getCause(): e;
}

Since exceptionally’s primary purpose is translating an exception to a non-exceptional result value, using it for translating the exception to another thrown exception was not the best fit anyway and it also needed an instanceof. So performing this translation in the catch clause saves you from another translation step.

like image 92
Holger Avatar answered Oct 28 '22 20:10

Holger


This is not possible. The Javadoc of join() clearly states:

Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally. To better conform with the use of common functional forms, if a computation involved in the completion of this CompletableFuture threw an exception, this method throws an (unchecked) CompletionException with the underlying exception as its cause.

(emphasis is mine)

like image 45
Didier L Avatar answered Oct 28 '22 20:10

Didier L