Why aren't my callback methods working properly? - java

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.

Related

Is CompletableFuture followed immediately by a get efficient?

I just found the following code, it supplies an asynchronous task but immediately get the result (so if I understand correctly, it blocks the current thread until the result is available).
Is it efficient ?
public String myMethod() {
CompletableFuture<String> futur = CompletableFuture.supplyAsync(() -> {
// my long call to an external API
return "theResult";
});
try {
return future.get(FUTURE_TIMEOUT_DURATION, TimeUnit.MINUTES);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
If the timeout is handled correctly in the call to the external API, do I need this completable futur ?
Is it ok to simplify the code like this ?
public String myMethod() {
// my long call to an external API
return "theResult";
}
If you doesn't expect any problem with timeout you most probably can remove code related to feature.
There is possibility that code use some threadlocal variables or otherwise relies on being executed in separate thread.

Spring Reactor: What's the corresponding class to Optional<T>?

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

Java FutureTask - Multithreaded call to get()

I have the following two methods in a class:
private MyDef myDef;
private FutureTask<MyDef> defFutureTask;
public synchronized void periodEviction() {
myDef = null;
}
public MyDef loadMyItems() {
// if it's not ready use a future - it will block until the results are ready
if (this.myDef == null) { // this will still not be thread safe
Callable<MyDef> callableDef = ()->{ return this.loadFromDatabase(); };
FutureTask<MyDef> defTask = new FutureTask<>(callableDef);
this.defFutureTask = defTask;
defFutureTask.run();
}
try {
// wait until's it's ready
this.myDef = this.qDefFuture.get();
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return this.myDef;
}
I wanted to do the following:
1) Do a cache eviction using periodEviction() every one hour or so.
2) Otherwise, use the cached value when db loading is done.
I believe I have misunderstood Java future as I couldn't answer the question, "What happens when Thread A,B,and C all are calling loadMyItems() at the same time?"
So does this mean without something like an executor, this implementation is still not thread safe?
An even simpler approach is to not cache the object at all but just retain the Future.
private CompletableFuture<MyDef> defFuture;
public synchronized void periodEviction() {
// evict by triggering the request anew
defFuture = CompletableFuture.supplyAsync(this::loadFromDatabase);
}
public synchronized Optional<MyDef> loadMyItems() {
try {
return Optional.of(this.defFuture.get());
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return Optional.empty();
}
With the caveat that this will trigger the database query every eviction period rather than on demand.
A super simple approach would be to declare loadMyItems as synchronized. But if the class has other methods that access myDef, you would have to declare those synchronized too. Sometimes this results in very coarse-grained locking and slower performance.
If you're looking for the cleanest/fastest code, instead of declaring periodEviction as synchronized, declare myDef as an AtomicReference:
private final AtomicReference<MyDef> myDef = new AtomicReference<>();
Then the body of periodEviction is:
synchronized (myDef) {
myDef.set(null);
}
And the body of loadMyItems is:
synchronized (myDef) {
if (myDef.get() == null) {
// perform initialization steps, ending with:
myDef.set(this.qDefFuture.get());
}
return myDef.get();
}
If many threads call loadMyItems at the same time, myDef will only ever be initialized once, and they will all get the same object returned (unless somehow a call to periodEviction snuck in the middle).

How to DRY exception handling with Java 8 CompletableFuture when code throws exception?

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

using Supplier in CompletableFuture yields different result than using lambda

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.

Categories