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?
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 RuntimeException rte? rte: 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.