I am having a problem dealing with exceptions throw from CompletableFuture methods. I thought it should be possible to throw an exception from within the exceptionally clause of a CompletableFuture. For example, in the method below, I expected that executeWork would throw a RuntimeException because I am throwing one in the various exceptionally clauses, however, this does not work and I'm not sure why.
public void executeWork() {
service.getAllWork().thenAccept(workList -> {
for (String work: workList) {
service.getWorkDetails(work)
.thenAccept(a -> sendMessagetoQueue(work, a))
.exceptionally(t -> {
throw new RuntimeException("Error occurred looking up work details");
});
}
}).exceptionally(t -> {
throw new RuntimeException("Error occurred retrieving work list");
});
}
You're doing a few things wrong here (async programming is hard):
First, as #VGR noted, executeWork() will not throw an exception when things go bad - because all the actual work is done on another thread. executeWork() will actually return immediately - after scheduling all the work but without completing any of it. You can call get() on the last CompletableFuture, which will then wait for the work completion, or failure, and will throw any relevant exceptions. But that is force-syncing and considered an anti-pattern.
Secondly, you don't need to throw new RuntimeException() from the exceptionally() handle - that one is actually called with the correct error (t) in your case.
looking at an analogous synchronous code, your sample looks something like this:
try {
for (String work : service.getAllWork()) {
try {
var a = service.getWorkDetails(work);
sendMessageToQueue(work, a);
} catch (SomeException e) {
throw new RuntimeException("Error occurred looking up work details");
}
}
} catch (SomeOtherException e) {
throw new RuntimeException("Error occured retrieving work list");
}
So as you can see, there's no benefit from catching the exceptions and throwing RuntimeException (which also hides the real error) instead of just letting the exceptions propagate to where you can handle them.
The purpose of an exceptionally() step is to recover from exceptions - such as putting default values when retrieving data from user or IO has failed, or similar things. Example:
service.getAllWork().thenApply(workList -> workList.stream()
.map(work -> service.getWorkDetails(work)
.thenAccept(a -> sendMessageToQueue(work, a)
.exceptionally(e -> {
reportWorkFailureToQueue(work, e);
return null;
})
)
).thenCompose(futStream ->
CompletableFuture.allOf(futStream.toArray(CompletableFuture[]::new)))
.exceptionlly(e -> {
// handle getAllWork() failures here, getWorkDetail/sendMessageToQueue
// failures were resolved by the previous exceptionally and converted to null values
});
Related
I've a method
private void disableXYZ(Long rId, List<Long> disableIds, String requestedBy) {
for (Long disableId : disableIds) {
try {
disablePackXYZ(UnSubscribeRequest.unsubscriptionRequest()
.requestedBy(requestedBy)
.cancellationReason("system")
.id(disableId)
.build());
} catch (Exception e) {
log.error("Failed to disable pack. id: {}, rId: {}. Error: {}", disableId, rId, e.getMessage());
}
}
}
I want to make parallel method calls to disablePackXYZ with ids in disableIds list instead of an iterative for loop. But doing so I don't want to abort all the parallel calls if exception is thrown by any one or more disablePackXYZ calls. I can catch or suppress the exception but it should not abort processing of all the other threads or of the same thread for further tasks. Please suggest the approach for the same.
I went through java streams approach over the web -
final CompletableFuture<ParseException> thrownException = new CompletableFuture<>();
Stream.of(columns).parallel().forEach(column -> {
try {
result[column.index] = parseColumn(valueCache[column.index], column.type);
} catch (ParseException e) {
thrownException.complete(e);
}});
For above approach, I found over web that if there is at least one exception thrown in a thread, the forEach invocation will propagate it (or one of them) to the caller. Hence, the current implementation doesn’t wait for the completion of all threads when it encounters an exception.
You've got a result[] array for storing results, just create an error[] array for storing exceptions. In your catch statement, store the exception into the error[] array and don't call thrownException.complete(e).
I'm just starting to get familiar with the CompletableFuture tool from Java. I've created a little toy application to model some recurrent use case almost any dev would face.
In this example I simply want to save a thing in a DB, but before doing so I want to check if the thing was already saved.
If the thing is already in the DB the flow (the chain of completable futures) should stop and not save the thing. What I'm doing is throwing an exception so eventually I can handle it and give a good message to the client of the service so he can know what happened.
This is what I've tried so far:
First the code that try to save the thing or throw an error if the thing is already in the table:
repository
.query(thing.getId())
.thenCompose(
mayBeThing -> {
if (mayBeThing.isDefined()) throw new CompletionException(new ThingAlreadyExists());
else return repository.insert(new ThingDTO(thing.getId(), thing.getName()));
And this is the test I'm trying to run:
CompletableFuture<Integer> eventuallyMayBeThing =
service.save(thing).thenCompose(i -> service.save(thing));
try {
eventuallyMayBeThing.get();
} catch (CompletionException ce) {
System.out.println("Completion exception " + ce.getMessage());
try {
throw ce.getCause();
} catch (ThingAlreadyExist tae) {
assert (true);
} catch (Throwable t) {
throw new AssertionError(t);
}
}
This way of doing it I took it from this response: Throwing exception from CompletableFuture ( the first part of the most voted answer ).
However, this is not working. The ThingAlreadyExist is being thrown indeed but it's never being handled by my try catch block.
I mean, this:
catch (CompletionException ce) {
System.out.println("Completion exception " + ce.getMessage());
try {
throw ce.getCause();
} catch (ThingAlreadyExist tae) {
assert (true);
} catch (Throwable t) {
throw new AssertionError(t);
}
is never executed.
I have 2 questions,
Is there a better way?
If not, am I missing something? Why can't I handle the exception in my test?
Thanks!
Update(06-06-2019)
Thanks VGR you are right. This is the code working:
try {
eventuallyMayBeThing.get();
} catch (ExecutionException ce) {
assertThat(ce.getCause(), instanceOf(ThingAlreadyExists.class));
}
By unit testing your code wrapped up in a Future, you’re testing java’s Future framework. You shouldn’t test libraries - you either trust them or you don’t.
Instead, test that your code, in isolation, throws the right exceptions when it should. Break out the logic and test that.
You can also integration test your app to assert that your entire app behaves correctly (regardless of implementation).
You have to be aware of the differences between get() and join().
The method get() is inherited from the Future interface and will wrap exceptions in an ExecutionException.
The method join() is specific to CompletableFuture and will wrap exceptions in a CompletionException, which is an unchecked exception, which makes it more suitable for the functional interfaces which do not declare checked exceptions.
That being said, the linked answer addresses use cases where the function has to do either, return a value or throw an unchecked exception, whereas your use case involves compose, where the function will return a new CompletionStage. This allows an alternative solution like
.thenCompose(mayBeThing -> mayBeThing.isDefined()?
CompletableFuture.failedFuture(new ThingAlreadyExists()):
repository.insert(new ThingDTO(thing.getId(), thing.getName())))
CompletableFuture.failedFuture has been added in Java 9. If you still need Java 8 support, you may add it to your code base
public static <T> CompletableFuture<T> failedFuture(Throwable t) {
final CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(t);
return cf;
}
which allows an easy migration to a newer Java version in the future.
I am trying to follow this tutorial JUnit 5: How to assert an exception is thrown?
I use Java 10, IntelliJ 2018 and Junit 5.
I make a calculator app that adds 2 fractions. It checks whether the input has 0 in the denominator.
When I run the test The exception Message get printed out "Undefined Math Expression" but my IDE says "Expected java.lang.Throwable to be thrown, but nothing was thrown." I think there is some problem with the scope of my code? I'm a newbie, please be kind. I provided the code and the test below:
public class Calculator {
public static int[] calculate (int firstNumerator, int firstDenominator, int secondNumerator, int secondDenominator) {
String exceptionMessage = "Undefined Math Expression";
int resultNumerator;
int resultDenominator;
int[] result = new int[2];
resultNumerator = (firstNumerator * secondDenominator) +
(secondNumerator * firstDenominator);
resultDenominator = firstDenominator * secondDenominator;
try {
if (resultDenominator == 0) {
throw (new Throwable(exceptionMessage));
} else {
result[0] = resultNumerator;
result[1] = resultDenominator;
}
} catch (Throwable e) {
System.out.println(e.getMessage());
}
return result;
}
}
The test:
class CalculatorTest {
#Test
void denominatorContainsZero() {
assertThrows(Throwable.class, () -> {
Calculator.calculate(0,0,0,0);
});
}
}
The misunderstanding here appears to be in what JUnit can actually see.
JUnit isn't magical: it's just plain old Java. It can't see inside your methods to see what they are doing. All it can see is what any other code can see when it executes a method: the return value and uncaught exceptions (as well as any side effects of the method, if they are visible to the calling code).
Your method here doesn't throw an exception from the perspective of a caller: internally, it throws the exception, but it catches and handles it.
If you want JUnit to test that an exception is thrown, you need to not catch that exception.
It is never (*) the right thing to do to throw an exception and then catch and handle it yourself. What's the point? You can simply do the thing you do to handle it, without throwing the exception. Exceptions are expensive to throw, because of the need to capture the entire stack trace.
Throwable is never (*) the right exception to throw. It's the exception "equivalent" of returning Object: it conveys no type information about the exception to the caller, who then either has to do a lot of work to try to handle it; or, more realistically, should just propagate it themselves. IllegalArgumentException is the right exception to throw here, if you actually needed to throw (and not catch) an exception.
Throwable is rarely the right thing to catch. Throwable is a supertype of both Exception and Error, so you might unintentionally catch an Error, like OutOfMemoryError, which shouldn't be caught because there is nothing reasonable to do except crash your program. Catch the most specific type you can; which also means that you should throw the most specific type you can (or, at least, a type appropriate to the abstraction).
(*) This is "never" as in "ok, there are a limited number of circumstances where it may be appropriate". But unless you understand what these are, don't.
The Throwable is catched by try catch block, so Junit can not access it. Try remove the try catch block.
You are not actually throwing exception, you are catching it. For this to work, you should remove try catch block.
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 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.
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)
As mentionend in the Subject: Java forces me to return something, because the handling of the Exception is in an own function.
public String returnLel(String mattDamon) {
try {
trick(mattDamon); // The LelException could be thrown
return "lel";
} catch (LelException e) {
handleException();
}
}
public void handleException() {
throw new RuntimeException();
}
`
Your code will not compile because the compiler is not "clever" enough to know you are throwing a RuntimeException in handleException.
In order for your code to compile you can either:
directly throw new RuntimeException(); in your catch statement (ugly)
return null after invoking handleException(); (acceptable, but still kind of ugly)
add a finally statement to finalize your method and return null or whatever if some condition has not been met (recommended)
Also note that I'm assuming LelException extends RuntimeException, otherwise your catch statement won't compile.
Well it is up to you on how you will handle the situation.
If you are able to handle the exception by say logging it and just return null as most of the comments imply that is totally o.k.
Just consider that this situation could also mean that the method "returnLel" might not be the correct place to handle this exception but to let the caller decide what to do in this situation by "throwing it further" like so:
public String returnLel(String mattDamon) throws LelException{
trick(mattDamon); // The LelException could be thrown
return "lel";
}
This means if the caller calls your returnLel-Method he will have to try/catch the Exception (or throw it further up) and could in this case mean a better design because the caller will not receive null or a String' but aStringor aLeLException` instead
Move the return statement out of the try catch.
The try block should surround the part where an exception could be thrown, what the return statement not does.
Here the java tutorials for some basic knowledge about this topic:
The try Block
The catch Block
The finally Block