I've been playing around with CompletableFuture and noticed a strange thing.
String url = "http://google.com";
CompletableFuture<String> contentsCF = readPageCF(url);
CompletableFuture<List<String>> linksCF = contentsCF.thenApply(_4_CompletableFutures::getLinks);
linksCF.thenAccept(list -> {
assertThat(list, not(empty()));
});
linksCF.get();
If, in my thenAccept call, the assertion fails, the exception is not propagated.
I tried something even uglier then:
linksCF.thenAccept(list -> {
String a = null;
System.out.println(a.toString());
});
nothing happens, no exception is propagated. I tried using methods like handle and others related to exceptions in CompletableFutures, but failed - none is propagating the exception as expected.
When I debugged the CompletableFuture, it does catch the exception like this:
final void internalComplete(T v, Throwable ex) {
if (result == null)
UNSAFE.compareAndSwapObject
(this, RESULT, null,
(ex == null) ? (v == null) ? NIL : v :
new AltResult((ex instanceof CompletionException) ? ex :
new CompletionException(ex)));
postComplete(); // help out even if not triggered
}
and nothing else.
I'm on JDK 1.8.0_05 x64, Windows 7.
Am I missing something here?
The problem is you never request to receive the results of your call to linksCF.thenAccept(..).
Your call to linksCF.get() will wait for the results of the execution in your chain. But it will only return the results of then linksCF future. This doesn't include the results of your assertion.
linksCF.thenAccept(..) will return a new CompletableFuture instance. To get the exception thrown call get() or check the exception status with isCompletedExceptionally() on the newly return CompletableFuture instance.
CompletableFuture<Void> acceptedCF = linksCF.thenAccept(list -> {
assertThat(list, not(empty()));
});
acceptedCF.exceptionally(th -> {
// will be executed when there is an exception.
System.out.println(th);
return null;
});
acceptedCF.get(); // will throw ExecutionException once results are available
Alternative?
CompletableFuture<List<String>> appliedCF = linksCF.thenApply(list -> {
assertThat(list, not(empty()));
return list;
});
appliedCF.exceptionally(th -> {
// will be executed when there is an exception.
System.out.println(th);
return Coolections.emptyList();
});
appliedCF.get(); // will throw ExecutionException once results are available
Although the question is basically already answered by Gregor Koukkoullis (+1), here is a MCVE that I created to test this.
There are several options for obtaining the actual exception that caused the problem internally. However, I don't see why calling get on the future that is returned by thenAccept should be an issue. In doubt, you could also use thenApply with the identity function and use a nice fluent pattern, like in
List<String> list =
readPage().
thenApply(CompletableFutureTest::getLinks).
thenApply(t -> {
// check assertion here
return t;
}).get();
But maybe there's a particular reason why you want to avoid this.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
public class CompletableFutureTest
{
public static void main(String[] args)
throws InterruptedException, ExecutionException
{
CompletableFuture<String> contentsCF = readPage();
CompletableFuture<List<String>> linksCF =
contentsCF.thenApply(CompletableFutureTest::getLinks);
CompletableFuture<Void> completionStage = linksCF.thenAccept(list ->
{
String a = null;
System.out.println(a.toString());
});
// This will NOT cause an exception to be thrown, because
// the part that was passed to "thenAccept" will NOT be
// evaluated (it will be executed, but the exception will
// not show up)
List<String> result = linksCF.get();
System.out.println("Got "+result);
// This will cause the exception to be thrown and
// wrapped into an ExecutionException. The cause
// of this ExecutionException can be obtained:
try
{
completionStage.get();
}
catch (ExecutionException e)
{
System.out.println("Caught "+e);
Throwable cause = e.getCause();
System.out.println("cause: "+cause);
}
// Alternatively, the exception may be handled by
// the future directly:
completionStage.exceptionally(e ->
{
System.out.println("Future exceptionally finished: "+e);
return null;
});
try
{
completionStage.get();
}
catch (Throwable t)
{
System.out.println("Already handled by the future "+t);
}
}
private static List<String> getLinks(String s)
{
System.out.println("Getting links...");
List<String> links = new ArrayList<String>();
for (int i=0; i<10; i++)
{
links.add("link"+i);
}
dummySleep(1000);
return links;
}
private static CompletableFuture<String> readPage()
{
return CompletableFuture.supplyAsync(new Supplier<String>()
{
#Override
public String get()
{
System.out.println("Getting page...");
dummySleep(1000);
return "page";
}
});
}
private static void dummySleep(int ms)
{
try
{
Thread.sleep(ms);
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
If, in my thenAccept call, the assertion fails, the exception is not propagated.
The continuation that you register with thenAccept() is a separate task from the linksCF future. The linksCF task completed successfully; there is no error for it to report. It has its final value. An exception thrown by linksCF should only indicate a problem producing the result of linksCF; if some other piece of code that consumes the result throws, that does not indicate a failure to produce the result.
To observe an exception that happens in a continuation, you must observe the CompletableFuture of the continuation.
correct. but 1) I should not be forced to call get() - one of the points of the new constructs; 2) it's wrapped in an ExecutionException
What if you wanted to hand the result off to multiple, independent continuations using thenAccept()? If one of those continuations were to throw, why should that impact the parent, or the other continuations?
If you want to treat linksCF as a node in a chain and observe the result (and any exceptions) that happen within the chain, then you should call get() on the last link in the chain.
You can avoid the checked ExecutionException by using join() instead of get(), which will wrap the error in an unchecked CompletionException (but it is still wrapped).
The answers here helped me to manage exception in CompletableFuture, using "exceptionnaly" method, but it missed a basic example, so here is one, inspired from Marco13 answer:
/**
* Make a future launch an exception in the accept.
*
* This will simulate:
* - a readPage service called asynchronously that return a String after 1 second
* - a call to that service that uses the result then throw (eventually) an exception, to be processed by the exceptionnaly method.
*
*/
public class CompletableFutureTest2
{
public static void main(String[] args)
throws InterruptedException, ExecutionException
{
CompletableFuture<String> future = readPage();
CompletableFuture<Void> future2 = future.thenAccept(page->{
System.out.println(page);
throw new IllegalArgumentException("unexpected exception");
});
future2.exceptionally(e->{
e.printStackTrace(System.err);
return null;
});
}
private static CompletableFuture<String> readPage()
{
CompletableFuture<String> future = new CompletableFuture<>();
new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// FUTURE: normal process
future.complete("page");
}).start();
return future;
}
}
The mistake to avoid is to call "exceptionnaly" on the 1st future (the variable future in my code) instead of the future returned by the "thenAccept" which contains the lambda that may throw an exception (the variable future2 in my code).
.
As usual, understanding the behavior of CompletableFuture is better left to the official docs and a blog.
Each then...() chaining method of the CompletableFuture class, which implements CompletionStage, accepts a an argument a CompletionStage. The stage that is passed depends on which order of then...() methods you've chained. Again, docs, but here's that aforementioned blog.
Related
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);
}
I don't know about you guys but, for me it become very annoying when I see a piece of code that is repeated and I came across the following scenario when using Services that throw exception. As shown below, in each CompletableFuture block I have to do exception handling and that part is basically repeated over and over depending on how many completable futures you are going to have.
CompletableFuture<Void> future1Of15 = CompletableFuture.supplyAsync(() -> {
List<SomePojo> somePojos = null;
try {
somePojos = someServiceThatThrowsException.getAll(SomePojo.class);
} catch (SomeException e) {
//Handle the exception
e.printStackTrace();
}
return somePojos;
}).thenAcceptAsync(result -> //do something with the result);
CompletableFuture<Void> future2Of15 = CompletableFuture.supplyAsync(() -> {
List<OtherPojo> otherPojos = null;
try {
otherPojos = someServiceThatThrowsException.getAll(OtherPojo.class);
} catch (SomeException e) {
//Handle the exception
e.printStackTrace();
}
return otherPojos;
}).thenAcceptAsync(result -> //do something with the result);
Now repeat the above x number of times and you notice that, the try/catch block is repeated. In my case, I have around 15-20 such calls.
Is there a way that I could turn the above into 1 or 2 lines of code? In order words stop repeating myself with regards to exception handling inside the supplyAsync lambda?
Just add a method to your class that does all of the repeated code, and takes a Consumer<List<?>> as an argument to pass to thenAcceptAsync on the last line.
private CompletableFuture<Void> getAndAcceptAsync(Consumer<List<?>> resultProcessor) {
return CompletableFuture.supplyAsync(() -> {
List<SomePojo> somePojos = null;
try {
somePojos = someServiceThatThrowsException.getAll(SomePojo.class);
} catch (SomeException e) {
//Handle the exception
e.printStackTrace();
}
return somePojos;
}).thenAcceptAsync(resultProcessor);
}
You can then call this as many times as you need to.
future1Of15 = getAndAcceptAsync(result-> { do something } );
future2Of15 = getAndAcceptAsync(result-> { do something else } );
There are patterns for handling effects like e.g. failure in functional programming. One such pattern is the Try monad for which e.g. vavr provides an implementation in java.
Those patterns abstract a lot of boilerplate away from you via declarative apis:
CompletableFuture
.supplyAsync(() -> Try.of(() -> methodThatThrows()))
.thenAccept(res -> System.out.println(res));
Or if you aren't bound to using CompletableFuture, you may choose to use the Future monad to further reduce boilerplate code:
Future.of(() -> methodThatThrows())
.onComplete(result -> System.out.println(result));
Either way, what you end up with as the result of that Future<T> is a Try<T>, which can be either a Success<T> or a Failure which you can deal with accordingly.
I do not claim that this is the only approach or the most effective approach, I am sharing the solution that helped me DRY and it might or might not work for you. If you have a better solution, please share it.
I created the following utility method
#Component
public class CompletableFutureUtil {
#Autowired
private SomeGenericServiceThatThrowsException genericService;
public <T> CompletableFuture<Collection<T>> fireupCompletableFutureAsynchronously(Class<T> type) {
CompletableFuture<Collection<T>> future = CompletableFuture.supplyAsync(() -> {
Collection<T> mystery = null;
try {
mystery = genericService.list(type);
} catch (SomeException e) {
e.printStackTrace();
//Handle the exception
}
return mystery;
});
return future;
}
}
And now I can reuse the above utility method in the following way after autowiring it
#Autowired private CompletableFutureUtil futureUtil;
The calls basically become one to two lines max
futureUtil.fireupCompletableFutureAsynchronously(SomePojo.class)
.thenAcceptAsync(result -> //do something with result);
futureUtil.fireupCompletableFutureAsynchronously(OtherPojo.class)
.thenAcceptAsync(result -> //do something with result);
Happy DRY+ing
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
}
I have created a small example of reading a text file and wrap the call with CompletableFuture.
public class Async {
public static void main(String[] args) throws Exception {
CompletableFuture<String> result = ReadFileUsingLambda(Paths.get("path/to/file"));
result.whenComplete((ok, ex) -> {
if (ex == null) {
System.out.println(ok);
} else {
ex.printStackTrace();
}
});
}
public static CompletableFuture<String> ReadFileUsingSupplier(Path file) throws Exception {
return CompletableFuture.supplyAsync(new Supplier<String>() {
#Override
public String get() {
try {
return new String(Files.readAllBytes(file));
} catch (IOException e) {
e.printStackTrace();
return "test";
}
}
}, ForkJoinPool.commonPool());
}
public static CompletableFuture<String> ReadFileUsingLambda(Path file) throws Exception {
return CompletableFuture.supplyAsync(() -> {
try {
return new String(Files.readAllBytes(file));
} catch (IOException e) {
e.printStackTrace();
return "test";
}
} , ForkJoinPool.commonPool());
}
}
This code returns nothing. It executes and "nothing happens", no errors or output. If I call ReadFileUsingSupplier instead of ReadFileUsingLambda then I get the file content printed in the console!
To me this doesn't make sense because a lambda is a shorthand for writing an inline function and it shouldn't change the behaviour but in this example it does apparently.
I think it's just a matter of execution timing - the lambda may take a little more to execute, allowing the program to exit before you are done reading the file.
Try this:
add a Thread.sleep(1000); as the first statement within the try block in ReadFileUsingSupplier and you won't see any output
add a Thread.sleep(1000); at the end of your main when using ReadFileUsingLambda and you will see the expected output
To make sure your main doesn't exit before the future is completed, you can call:
result.join();
As noted, you need to result.join() in either case to avoid the main thread exiting too quickly.
It seems that there's a penalty for using lambdas vs anonymous closures while the JVM warms up, thereafter the performance is the same. I found this information at on another SO thread - which in turn links a performance study by Oracle.
As a sidenote it's not a great idea to Thread.sleep() to fix weird timing issues, ever. Figuring out the cause and applying the appropriate measures would be much clearer when re-read by you or by others, e.g.
System.out.println(result.get(5, TimeUnit.SECONDS));
This enables you to ditch the .join(), too.
I'm working on a project that involves Hystrix, and I decided to use RxJava. Now, forget Hystrix for the rest of this because I believe the main problem is with my complete screwing up of writing the Observable code correctly.
Need:
I need a way to return an observable that represents a number of observables, each running a user task. I want that Observable to be able to return all results from the tasks, even errors.
Problem:
Observable streams die on errors. If I have three tasks and the second task throws an exception, I never receive the third task even if it would have succeeded.
My Code:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return Observable
.from(tasks)
.flatMap(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task).toObservable().subscribeOn(this.schedulerFactory.get(groupName));
} catch(Exception ex) {
return Observable.error(ex);
}
});
}
Given that MyCommand is a class that extends HystrixObservableCommand, it returns an Observable and so shouldn't figure in on the problems I'm seeing.
Attempt 1:
Used Observable.flatMap as above
Good: Each Command is scheduled on it's own thread and the tasks run asynchronously.
Bad: On first Command exception, Observable completes having emitted previous successful results and emitting the Exception. Any in-flight Commands are ignored.
Attempt 2:
Used Observable.concatMapDelayError instead of flatMap
Bad: For some reason, tasks run synchronously. Why??
Good: I get all the successful results.
~Good: OnError gets a Composite exception with a list of the exceptions thrown.
Any help will be greatly appreciated and probably result in me being very embarrassed for not having thought of it myself.
Additional Code
This test succeeds with Observable.flatMap, but fails when using Observable.concatMapDelayError because the tasks do not run asynchronously:
java.lang.AssertionError: Execution time ran over the 350ms limit: 608
#Test
public void shouldRunManagedAsyncTasksConcurrently() throws Exception {
Observable<String> testObserver = executor.observeManagedAsync("asyncThreadPool",getTimedTasks());
TestSubscriber<String> testSubscriber = new TestSubscriber<>();
long startTime = System.currentTimeMillis();
testObserver.doOnError(throwable -> {
System.out.println("error: " + throwable.getMessage());
}).subscribe(testSubscriber);
System.out.println("Test execution time: "+(System.currentTimeMillis()-startTime));
testSubscriber.awaitTerminalEvent();
long execTime = (System.currentTimeMillis()-startTime);
System.out.println("Test execution time: "+execTime);
testSubscriber.assertCompleted();
System.out.println("Errors: "+testSubscriber.getOnErrorEvents());
System.out.println("Results: "+testSubscriber.getOnNextEvents());
testSubscriber.assertNoErrors();
assertTrue("Execution time ran under the 300ms limit: "+execTime,execTime>=300);
assertTrue("Execution time ran over the 350ms limit: "+execTime,execTime<=350);
testSubscriber.assertValueCount(3);
assertThat(testSubscriber.getOnNextEvents(),containsInAnyOrder("hello","wait","world"));
verify(this.mockSchedulerFactory, times(3)).get("asyncThreadPool");
}
Tasks for the above unit test:
protected List<EspTask<String>> getTimedTasks() {
EspTask longTask = new EspTask("helloTask") {
#Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(100);
return "hello";
}
};
EspTask longerTask = new EspTask("waitTask") {
#Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(150);
return "wait";
}
};
EspTask longestTask = new EspTask("worldTask") {
#Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(300);
return "world";
}
};
return Arrays.asList(longTask, longerTask, longestTask);
}
You can use Observable.onErrorReturn(), and return special value (e.g. null), then filter non-special values downstream. Keep in mind that source observable will complete on error. Also depending on use case Observable.onErrorResumeNext()methods can be useful aswell. If you are interested in error notifications, use Observable.materialize(), this will convert items and onError(), onComplete() into Notifications, which then can be filtered by Notification.getKind()
Edit.
All operators mentioned above should be added right after .toObservable().subscribeOn(this.schedulerFactory.get(groupName)); assuming try/catch was absent.
You want to use mergeDelayError:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return Observable.mergeDelayError(Observable
.from(tasks)
.map(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task).toObservable().subscribeOn(this.schedulerFactory.get(groupName));
} catch(Exception ex) {
return Observable.error(ex);
}
}));
}
Note that your MyCommand constructor should not throw any exceptions; this allows your code to be written more concisely:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return from(tasks)
.map(task -> new MyCommand(task.getTaskId(), groupName, task)
.toObservable()
.subscribeOn(this.schedulerFactory.get(groupName)))
.compose(Observable::mergeDelayError);
}
Keep in mind that this will still invoke onError at most once; if you need explicit handling of all errors, use something like an Either<CommandResult, Throwable> as the return type (or handle the errors and return an empty observable).
Use .materialize() to allow all emissions and errors to come through as wrapped notifications then deal with them as you wish:
.flatMap(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task)
.toObservable()
.subscribeOn(this.schedulerFactory.get(groupName))
.materialize();
} catch(Exception ex) {
return Observable.error(ex).materialize();
}
});