Completable futures. What's the best way to handle business "exceptions"? - java

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.

Related

why is the Catch exception e printing instead of doing what is in Try

I am confused why it is going to catch and print the error occurred message when I run my code since it runs and just prints the message I can't tell what the error could be
Note: this is my very first time using try and catch my professor told us to use it but haven't really learned how it works fully yet so I'm not sure if the problem is with that or if it's just with my code
public static void main (String [] args) {
try {
File file = new File("in.txt");
Scanner scanFile = new Scanner(file);
...
} catch(Exception e) {
System.out.println("Error");
}
}
Do not catch an exception unless you know what to do. It is common that you don't - most exceptions aren't 'recoverable'.
So, what do you do instead?
Simplest plan: Just tack throws Exception on your main method. public static void main(String[] args) methods should by default be declared to throws Exception, you need a pretty good reason if you want to deviate from this.
Outside of the main method, think about your method. Is the fact that it throws an exception an implementation detail (a detail that someone just reading about what the method is for would be a bit surprised by, or which could change tomorrow if you decide to rewrite the code to do the same thing but in a different way)? In that case, do not add that exception to the throws clause of your method. But if it is, just add it. Example: Any method whose very name suggests that file I/O is involved (e.g. a method called readFile), should definitely be declared to throws IOException. It'd be weird for that method not to throws that.
Occasionally you can't do that, for example because you're overriding or implementing a method from an interface or superclass that doesn't let you do this. Or, it's an implementation detail (as per 2). The usual solution then is to just catch it and rethrow it, wrapped into something else. Simplest:
} catch (IOException e) {
throw new RuntimeException("uncaught", e);
}
Note that the above is just the best default, but it's still pretty ugly. RuntimeException says very little and is not really catchable (it's too broad), but if you don't really understand what the exception means and don't want to worry about it, the above is the correct fire-and-forget. If you're using an IDE, it probably defaults to e.printStackTrace() which is really bad, fix that template immediately (print half the details, toss the rest in the garbage, then just keep on going? That's.. nuts).
Of course, if you know exactly why that exception is thrown and you know what to do about it, then.. just do that. Example of this last thing:
public int askForInt(String prompt) {
while (true) {
System.out.println(prompt + ": ");
try {
return scanner.nextInt();
} catch (InputMismatchException e) {
System.out.println("-- Please enter an integral number");
}
}
}
The above code will catch the problem of the user not entering an integer and knows what to do: Re-start the loop and ask them again, until they do it right.
Just add throws Exception to your main method declaration, and toss the try/catch stuff out.
There are a couple of problems with your current code:
catch (Exception e) {
System.out.println("Error occured...");
}
Firstly, you are not printing any information about the exception that you caught. The following would be better:
catch (Exception e) {
System.out.println(e.getMessage()); // prints just the message
}
catch (Exception e) {
System.out.println(e); // prints the exception class and message
}
catch (Exception e) {
e.getStackTrace(System.out); // prints the exception stacktrace
}
Secondly, for a production quality you probably should be logging the exceptions rather than just writing diagnostics to standard output.
Finally, it is usually a bad idea to catch Exception. It is usually better to catch the exceptions that you are expecting, and allow all others to propagate.
You could also declare the main method as throws Exception and don't bother to catch it. By default, JVM will automatically produce a stacktrace for any uncaught exceptions in main. However, that is a lazy solution. And it has the disadvantage that the compiler won't tell you about checked exceptions that you haven't handled. (That is a bad thing, depending on your POV.)
now null printed out.
This is why the 2nd and 3rd alternatives are better. There are some exceptions that are created with null messages. (My guess is that it is a NullPointerException. You will need a stacktrace to work out what caused that.)
Catching Exception e is often overly broad. Java has many built-in exceptions, and a generic Exception will catch all of them. Alternatively, consider using multiple catch blocks to dictate how your program should handle the individual exceptions you expect to encounter.
catch (ArithmeticException e) {
// How your program should handle an ArithmeticException
} catch (NullPointerException e) {
// How your program should handle a NullPointerException
}

Try Catch block works but test assertThrows fail (Junit 5)

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.

Throwing exceptions from within CompletableFuture exceptionally clause

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

Difference between Exceptions and if then in Java

I have 2 examples:
void terminate(String x) {
System.exit(0);
}
void x (long y) {
if(y == null) {
terminate();
}
//code
}
.....
x(null); //Don't want that
This example seems to work. I want to make sure that long y is not null. I might do this instead:
void x (long y) throws NullPointerException {
//code
}
...
try {
x(null);
} catch (NullPointerException e) {
e.printstacktrace();
}
What would the difference in performance be? In such example, it might not be visible, but in a complex program, would it matter? Should I get into practice of exceptions instead of if then?
Use exceptions for exceptional circumstances, when things happen that youdid not expect and where nothing sensible can be done to recover.
Otherwise, use if-then and maybe return a status code.
In terms of performance, exceptions create a huge overhead compared to returns. Calling a method and catching an exception if it didn't work is verbose and annoying.
void myOperation() {
step1();
step2();
step3();
}
In this example, exceptions are preferable for when any of the steps fails, the entire operation fails anyways, so I prefer not having to deal with any of this.
void myOperation(String number) {
int n;
try {
n = Integer.parse(number);
} catch (NumberFormatException e) {
n = DEFAULT;
}
doThings(n);
}
In this case, having to catch the exception is annoying because I kind of expect the string to be invalid and have a reasonable alternative. The exception makes the code slow and verbose.
What would the difference in performance be?
Version with exception definitely will works slower and will have overhead of processing exceptions, creating objects, filling stack traces and so on.
In such example, it might not be visible, but in a complex program,
would it matter? Should I get into practice of exceptions instead of if then?
In complex programs none use this kind of processing possible null pointer exceptions, and it's actually anti-pattern in java, so if you whant be save with null you should you kind of not null check like if(x!=null) or assert.
Also I advice you read more about exceptions in java and differences between checked/unchecked exceptions and so on.
PS here is good topic about null checks.

How do i handle the exception in an own function that Java compiles again?

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

Categories