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
}
Related
I'm trying to get into CompletableFuture class for a project I'm running, and I got to some question here:
There is the following method: it tries to find a conversation by its ID or hash; and, if not found, it throws an exception. So far, so good.
public ConversationOutput getConversationByIdOrHash(String conversationIdOrHash)
throws ConversationNotFoundException {
Conversation conversation = this.conversationRepository.getByIdOrHash(conversationIdOrHash);
if (conversation == null) {
throw new ConversationNotFoundException(conversationIdOrHash);
}
return this.modelMapper.map(conversation, ConversationOutput.class);
}
Note that I am throwing ConversationNotFoundException from my method signature. My SpringBoot controller is reacting to this exception and it's all working fine since the beginning.
What I'm trying to do is to make this to a CompletableFuture return and actually throwing an exception, something similar to:
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash)
throws ConversationNotFoundException {
return CompletableFuture.supplyAsync(() -> this.getConversationByIdOrHash(conversationIdOrHash));
}
I've seen posts where people use exceptionally to handle exceptions, but what I really want to do is to throw it to my controller and let it handle it. Any suggestions of how can I make it?
Thank you all!
The question is do you care about the result of CompletableFuture.
CompletableFuture is like a special task and it is processed on other thread. If you don't invoke .join() you won't receive the results of CompletableFuture. This method also will propagate the exception if any occured. However it waits for CompletableFuture to finish and blocks the request.
However, there is no way to get exceptions from the inside of the CompletableFuture without waiting, you have to treat it like other task.
You can pass the completed future in case of a success, and failed future along with your custom exception.
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash) {
try {
return CompletableFuture.completedFuture(this.getConversationByIdOrHash(conversationIdOrHash));
} catch (ConversationNotFoundException e) {
return CompletableFuture.failedFuture(e);
}
}
and then at your controller level you can handle the exception.
final CompletableFuture<ConversationOutput> future = getConversationByIdOrHashAsync("idOrHash");
future.whenComplete((r, e) -> {
if (e != null) {
if (e instanceof ConversationNotFoundException) {
//handling
}
}
});
So I have a Flux<Foo> and I want to map each Foo to Baz. The thing is, getBaz(Foo foo) may throw an IOException.
So I thought about having Mono<Baz> getBazRx(Foo foo) method which will return either a Mono.just(baz) or Mono.empty() in case of an exception.
Then will end up with Flux<Mono<Baz>> which kind of reminds the Optional<T> container.
Is that the way doing that in Spring Reactor? How to consume it properly?
In reactive streams, "optionals" are generally handled by removing absent elements from the stream (e.g. an empty Mono, or a Flux with the element dropped.), rather than having a Flux<Optional>, Mono<Optional>, or Flux<Mono>
When calling a synchronous getBaz method, you can use a single .handle operation, like this:
flux
.handle((foo, sink) -> {
try {
// propagate Baz down the stream
sink.next(getBaz(foo));
} catch (IOException e) {
// Since sink.next is not called here,
// the problematic element will be dropped from the stream
log.error(e);
}
})
When calling an asynchronous getBazRx method (returning Mono), you can use onErrorResume inside a flatMap/flatMapSequential/concatMap, like this:
flux
.flatMap(foo -> getBazRx(foo)
.onErrorResume(t -> {
log.error(t);
return Mono.empty();
}))
(Or you could move .onErrorResume inside .getBazRx, depending on where you want to catch and ignore the exception)
Also, since you alluded to it in your question... if you were to create getBazRx that wraps getBaz, you should never do something like this if getBaz has the potential to block:
Mono<Baz> getBazRx(Foo foo) {
// BAD!!!
try {
return Mono.just(getBaz(foo));
} catch (IOException e) {
return Mono.error(e) // or Mono.empty() if you want to ignore
}
}
That implementation is really just a synchronous method impersonating an async method. There are two problems with it:
Work is done immediately, instead of after subscribing to the returned Mono
If getBaz blocks, you could end up blocking the event loop
Instead, you should defer work until the mono is subscribed, and run any blocking operation on a Scheduler intended for blocking operations, like this:
Mono<Baz> getBazRx(Foo foo) {
return Mono.fromSupplier(() -> {
try {
return getBaz(foo);
} catch (IOException e) {
throw Exceptions.propagate(e); // or return null to ignore and complete empty
}
})
.subscribeOn(Schedulers.elastic()); // run on a scheduler suitable for blocking work
}
Since you want to skip the error (just log it for instance), you can use onErrorContinue. Also, since getBaz throws a checked exception, we need to catch it and return (not throw) a RuntimeException instead. Reactor has a utility method to do this Exceptions.propagate:
flux
.map(foo -> {
try {
return getBaz(foo);
} catch (IOException e) {
return Exceptions.propagate(e);
}
})
.onErrorContinue(RuntimeException.class, (t, b) -> log.error(t))
.subscribe(baz -> log.info("Read value {}", baz));
I use AsyncRestTemplate to make resttemplate asynchronously.
These methods should wait all asyncresttemplate processes till done, And It will return reviewContent.
Problem is callback methods are not working, before the entire method works done. So I can't take proper return value of optionName and membershipGradeCode and reviewType should be included in reviewContent.
Could someone explain what am I missing now?
rev#1
Success callback methods change the state of reviewContent, Could it be a problem?
public ReviewContent getRepresentativeReviewContent(Long dealNo, Long categoryNo, String setId) {
Optional<Map<String, Object>> review = Optional.ofNullable(boardApi.getRepresentativeReviewContent(dealNo));
if (review.isPresent()) {
Long memberNo = Long.valueOf(review.get().get("memberNo").toString());
ReviewContent reviewContent = new ReviewContent();
ListenableFuture<ResponseEntity<Map>> optionInfo = dealApi.asyncGetDealOption(Long.valueOf(review.get().get("optionNo").toString()));
optionInfo.addCallback(success -> {
try {
reviewContent.setOptionName((String) ((Map<String, Object>) success.getBody().get("data")).get("dealTitle"));
} catch (Exception e) {
reviewContent.setOptionName(null);
}
}, failure -> LOGGER.error("asyncGetDealOption", failure.getStackTrace()));
ListenableFuture<ResponseEntity<Map>> gradeInfoOfThisMember = mktApi.asyncGetMembershipGradeOfThisMember(memberNo);
gradeInfoOfThisMember.addCallback(success -> {
try {
reviewContent.setMembershipGradeCode((Integer) ((Map<String, Object>) success.getBody().get("data")).get("grade"));
} catch (Exception e) {
reviewContent.setMembershipGradeCode(0);
}
},
failure -> {
reviewContent.setMembershipGradeCode(0);
LOGGER.error("asyncGetMembershipGradeOfThisMember", failure.getStackTrace());
});
ListenableFuture<ResponseEntity<ReviewType>> reviewTypeByCategoryNo = boardApi.asyncGetReviewTypeByCategoryNo(categoryNo, setId);
reviewTypeByCategoryNo.addCallback(success -> {
try {
reviewContent.setReviewType(success.getBody());
} catch (Exception e) {
reviewContent.setReviewType(null);
}
},
failure -> {
reviewContent.setReviewType(null);
LOGGER.error("asyncGetReviewTypeByCategoryNo", failure.getStackTrace());
});
reviewContent.setReviewCount((Integer) review.get().get("reviewCount"));
reviewContent.setReviewAvgScore((Double) review.get().get("reviewAvgScore"));
reviewContent.setContents((String) review.get().get("contents"));
reviewContent.setCreateDt((String) review.get().get("createdDt"));
reviewContent.setUpdateDt((String) review.get().get("updatedDt"));
reviewContent.setMemberSrl(memberNo);
reviewContent.setTitle((String) review.get().get("title"));
reviewContent.setAccountSrl(Long.valueOf(review.get().get("accountNo").toString()));
reviewContent.setMemberId((String) review.get().get("memberId"));
reviewContent.setAccountSrl(Long.valueOf(review.get().get("accountNo").toString()));
boolean isApiExecutionDone = false;
while (!isApiExecutionDone) {
if (gradeInfoOfThisMember.isDone() && optionInfo.isDone() && reviewTypeByCategoryNo.isDone()) {
isApiExecutionDone = true;
}
}
return reviewContent;
}
return new ReviewContent();
}
So your problem is that the callbacks set properties on the object returned by your method. However, they are also executed asynchronously, and are not part of the done status of the Future: they are themselves executed once the Future is done, concurrently with the code in the getRepresentativeReviewContent method. Since the method returns as soon as all Futures are done, the properties aren't (all) set as they should.
Moreover, you didn't show the code for your ReviewContent object, but I'm pretty sure it doesn't declare the optionType, membershipGradeCode or reviewType fields as volatile. Since there are no barriers (such as synchronized blocks or Locks) in the method, there's no guarantee in the Java Memory Model that the values set in the callbacks (i.e. in other threads) would be seen in the thread executing the getRepresentativeReviewContent method.
Callbacks should only be used for side-effects outside of your main execution path, since it's hard to coordinate with them: you would have to use things like a CountDownLatch to make sure they have all executed, that would make the code even more complex.
Just wait for the asynchronous results in a straight-forward way (the code is untested though):
try {
// Not sure why you need to catch Exception here?
// If it's for flow control (absent entry in a Map), it's a bad practice.
// Just check it instead of catching NullPointerException.
reviewContent.setOptionName((String)
((Map<String, Object>) optionInfo.get().getBody().get("data"))
.get("dealTitle"));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.error("asyncGetDealOption", e);
reviewContent.setOptionName(null);
} catch (CancellationException | ExecutionException e) {
LOGGER.error("asyncGetDealOption", e);
reviewContent.setOptionName(null);
}
Another option is to compose the Futures, such as what can be done with Guava's Futures.transform to actually get the string you need out of the complete response, so you can just call get() on that composed Future to set your property. You'd still have to managed the errors, though.
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.
The scenario is as follows.
I implement an interface reading like this:
public interface MessageSourceProvider
{
MessageSource getMessageSource(Locale locale);
}
There are two implementations of this interface: one which reads from static sources, which are fully initialized at "constructor time", and one which does not; this latter implementation goes like this (expiryEnabled is an AtomicBoolean; comments removed, full source here; sources is a Map<Locale, FutureTask<MessageSource>>):
#Override
public MessageSource getMessageSource(final Locale locale)
{
if (!expiryEnabled.getAndSet(true))
setupExpiry(expiryDuration, expiryUnit);
FutureTask<MessageSource> task;
synchronized (sources) {
task = sources.get(locale);
if (task == null || task.isCancelled()) {
task = loadingTask(locale);
sources.put(locale, task);
service.execute(task);
}
}
try {
final MessageSource source = task.get(timeoutDuration, timeoutUnit);
return source == null ? defaultSource : source;
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt(); // <-- HERE
return defaultSource;
} catch (ExecutionException ignored) {
return defaultSource;
} catch (TimeoutException ignored) {
task.cancel(true);
return defaultSource;
} catch (CancellationException ignored) {
return defaultSource;
}
}
As the interface itself does not declare to throw InterruptedException (since some implementations will never do so), I do Thread.currentThread.interrupt(). I do this for the sake of conforming to the interface. In turn, implementations of this interface are used as such in the "main", user facing class:
public String getMessage(final Locale locale, final String key)
{
BUNDLE.checkNotNull(key, "query.nullKey");
BUNDLE.checkNotNull(locale, "query.nullLocale");
String ret;
MessageSource source;
for (final Locale l: LocaleUtils.getApplicable(locale))
for (final MessageSourceProvider provider: providers) {
source = provider.getMessageSource(l);
if (source == null)
continue;
ret = source.getKey(key);
if (ret != null)
return ret;
}
// No source found which has the key... Return the key itself.
return key;
}
Now, the problem is with the FutureTask. As .get() is blocking, an InterruptedException can be thrown. And since the base interface does not declare to throw the exception, if I catch one I choose to restore the thread interruption status.
Literature, however, disagrees: it says that you should only do that, or ignore the exception, only in the event that you are within a Thread that you created yourself.
My question is: this being a user facing API, is there a potential problem with the way I am currently dealing with this? If yes, how do I fix that?
Now, the problem is with the FutureTask. As .get() is blocking, an InterruptedException can be thrown. And since the base interface does not declare to throw the exception, if I catch one I choose to restore the thread interruption status.
This is exactly correct. You should always do it that way.
try {
...
} catch (InterruptedException e) {
// InterruptedException clears interrupt flag
// I always re-interrupt the thread first
Thread.currentThread().interrupt();
// then i decide if I want the thread to return or throw or ...
return;
}
I would say also that you should think about what it means for your thread to be interrupted. Someone interrupted the thread that is calling future.get(). How should that thread respond?
Should it throw a RuntimeException?
Should it stop doing what it is doing and quit?
Should it shutdown the thread-pool?
Literature, however, disagrees: it says that you should only do that, or ignore the exception, only in the event that you are within a Thread that you created yourself.
I'm not sure what literature you are looking at but IMO it is incorrect or is being imprecise.
Restoring the interrupt flag is perfectly legitimate, as discussed in Java Concurrency in Practice, for example.