CompletableFuture handle and completeExceptionally cannot work together? - java

I am trying to set a default value when exceptions happen in CompletableFuture I made it work by handle method as follows:
private static void testHandle() {
String name = null;
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
}).handle((s, t) -> s != null ? s : "Hello, Stranger!" + t.toString());
out.println(completableFuture.join());
}
But when I tried to stop the CompletableFuture using completeExceptionally when bad things happen and track the exception as follows I cannot catch the exception as I did just now.
private static void testCompleteExceptionally() {
String name = "Hearen";
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
}).handle((s, t) -> {
try {
throw t.getCause();
} catch (Throwable e) {
out.println(e.toString()); // I was hoping to record the custom exceptions here;
}
return s != null ? s : "Hello, Stranger!" + t.toString();
});
if (name != null) {
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!")); // when bad things happen, I try to complete it by exception;
}
out.println(completableFuture.join());
}
UPDATED 2018-06-09 Thanks for the help, #Daniele
private static void testCompleteExceptionally() {
String name = "Hearen";
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
});
if (name != null) {
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
}
out.println(completableFuture.handle((s, t) -> s != null ? s : "Hello, Stranger!" + t.toString()).join());
}
The handle enclosed just before join() works as expected. But in this case, the returned value will be null.
Based on the handle API
Returns a new CompletionStage that, when this stage completes either normally or exceptionally, is executed with this stage's result and exception as arguments to the supplied function.

You are building a future, piping with an handle (so getting another future) and then completing exceptionally the future returned by the handle.
You should complete exceptionally the inner future itself, instead of the handle.
The point here is that handle returns another future; and you should not complete the "outer" future exceptionally, because doing that will bypass the handling behavior.
Below the code;
package stackOv;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class TestHandle {
BiFunction<String, Throwable, String> handle2 = new BiFunction<String, Throwable, String>() {
#Override
public String apply(String s, Throwable t) {
try {
throw t.getCause();
} catch (Throwable e) {
// I was hoping to record the custom exceptions here;
System.out.println(e.toString());
}
return s != null ? s : "Hello, Stranger!" + t.toString();
}
};
private void testCompleteExceptionally() {
String name = "Hearen";
Supplier<String> supplier2 = () -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
};
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(supplier2);
if (name != null) {
// when bad things happen, I try to complete it by exception;
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
}
System.out.println(completableFuture.handle(handle2).join());
}
public static void main(String[] args) {
TestHandle th = new TestHandle();
th.testCompleteExceptionally();
}
private static void delay(long milli) {
try { Thread.sleep(milli); } catch (InterruptedException e) {}
}
}

Related

Return the future that gets executed first with a specific condition on the response

I am trying to make 3 rest calls using completablefutures and return for the first one that matches a specific response. Below is sample test code I wrote (Minus the rest calls) for it but that does not seem to work. I always see "future1" getting returned even with the wait time, which means, test2 and test3 are blocking. How do I achieve the ask?
I thought of using CompletableFuture.anyOf but that just returns the result for the first future that gets executed. Not the first one that matches a specified response. Please advise
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Testing {
public static void main(String args[]) throws InterruptedException, ExecutionException {
CompletableFuture<String> combinedFuture = test("future1", 10000)
.thenCompose(response1 -> test2()
.thenCompose(response2 -> test3()
.thenApply(response3 -> {
return combine(response1, response2, response3);
})));
System.out.println(combinedFuture.get());
}
private static CompletableFuture<String> test(String str, int i) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
return str;
});
}
private static CompletableFuture<String> test2() {
return test("future2", 0);
}
private static CompletableFuture<String> test3() {
return test("future3", 0);
}
private static String combine(String response1, String response2, String response3) {
String responseString = null;
if (response1 != null) {
return response1;
} else if (response2 != null) {
return response2;
} else if (response3 != null) {
return response3;
}
return responseString;
}
}
You need to have a specific task that will collect the result of the others and complete only when desired.
For instance:
public class Testing {
public static void main(String args[]) throws InterruptedException, ExecutionException {
String result = aggregator(
Arrays.asList(
test("future1", 10000),
test("future2", 0),
test("future3", 0)),
(value) -> { return value != null; },
"default value"
).get();
System.out.println(result);
}
private static CompletableFuture<String> aggregator(Collection<CompletableFuture<String>> tasks, Predicate<String> validator, String defaultValue)
{
CompletableFuture<String> a = new CompletableFuture<String>();
AtomicInteger count = new AtomicInteger(0);
tasks.forEach((t) -> {
t.whenComplete((value, error) -> {
int c = count.incrementAndGet();
if( error == null && validator.test(value) ) a.complete(value);
else if( c == tasks.size() ) a.complete(defaultValue);
});
});
return a;
}
private static CompletableFuture<String> test(String str, int i) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
return str;
});
}
}
The aggregator method will accept any number of other tasks and will compare the result of each using the validator provided. The first one that matches is returned immediately without waiting for the others.
And at the end, if none matched, it completes with the default value.
You can race the futures against each other and delegate completions to another one:
static <T> CompletableFuture<T> first(Stream<CompletableFuture<T>> futures) {
var delegate = new CompletableFuture<T>();
runAsync(() ->
futures.forEach(future ->
future.handle(
(value, error) -> {
if (value == null) {
return delegate.completeExceptionally(error);
} else {
return delegate.complete(value);
}
})));
return delegate;
}
The future returned by first completes (either successfully or with an error), whenever the first one passed in the futures argument completes.
Now
CompletableFuture<String> combinedFuture =
first(Stream.of(test("future1", 10000), test2(), test3()));
System.out.println(combinedFuture.get());
prints either "future2" or "future3" depending on which on happens to complete first.

CompletableFuture recursive restart on exception from exceptionaly() block

Having a hard time to do a reliable retry of a background task which sends request to let's say mail service in order to get latest emails. Once emails successfully received the execution should continue in thenAccept() block - persist emails, however if exception occurs I have to rerun mail retrieval until successful attempt and on success should persist mails and stop. Please take a look and advice if I do it wrong.
private void retrieveMailsAsync(User user) {
CompletableFuture.supplyAsync(() -> {
try {
return mailService.getEmails(user.getName(), user.getPassword());
} catch (InvalidAuthentication | TimeoutException | BadGatewayException e) {
throw new CompletionException(e);
}
}).thenAccept(email -> {
mailService.persist(email);
}).exceptionally(ex -> {
log.log(Level.SEVERE, "Exception retrieveMailsAsync emails, Retrying retrieveMailsAsync:: ", ex.getCause());
retrieveMailsAsync(user);
return null;
});
}
P.S please also take a look at how I'm handling checked exception wrapping it into CompletionException and rethrowing - the main idea here to handle all exceptions (defined checked and runtime) in one exceptionally() block rather than logging them in catch block and return null.
Thanks guys in advance, hope I'm not doing pretty stupid stuff, or at least there is already reliable solutions exists for Java 8.
What I meant in my comment was this:
private void retrieveMailsAsync(User user) {
CompletableFuture.supplyAsync(() -> {
while (continueQuery()) { // true for infinite retries, or some other logic
try {
return mailService.getEmails(user.getName(), user.getPassword());
} catch (InvalidAuthentication | TimeoutException | BadGatewayException e) {
log.log(Level.SEVERE, "Exception retrieveMailsAsync emails, Retrying retrieveMailsAsync: ", e);
}
}
return null;
}).thenAccept(email -> {
mailService.persist(email);
});
}
Ie you just retry in the submitted runnable until you don't get an exception anymore.
I think you can achieve that via :
public static void main(String[] args) {
String result = call(new User().setName("name").setPassword("p")).join();
System.out.println(result);
}
private static CompletableFuture<String> call(User user) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> retrieveMailsAsync(user));
return future.handleAsync((String result, Throwable ex) -> {
// or any other Predicate that is satisfied against ex
if(ex != null) {
return call(user);
} else {
return future;
}
}).thenCompose(Function.identity());
}
EDIT
So what stays in your way to change the code above, for example, to:
static ExecutorService service = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
call(new User().setName("name").setPassword("p"))
// chain any other action here, like mailService.persist(email);
.thenAcceptAsync(
System.out::println,
service
);
System.out.println("Continue main thread");
}
private static CompletableFuture<String> call(User user) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> retrieveMailsAsync(user), service);
return future.handleAsync((String result, Throwable ex) -> {
// or any other Predicate that is satisfied against ex
if(ex != null) {
return call(user);
} else {
return future;
}
}).thenCompose(Function.identity());
}

Unit testing a void method with Redis async command in java

I'm trying to write a unit test for the following function.
responseHandler and ErrorHandler are both methods that are passes using the Command Pattern
and are used to continue the program's flow.
futureCommand is a lettuce object (Redis implementation in java).
I'm having difficulties with how to test this method since is both using a future and does not return anything.
public void getOfferData(EventRequestContext<? extends BaseEvent> ctx, int offerId, ResponseHandler<T1Offer> responseHandler,
ErrorHandler<Throwable> errorHandler) throws Exception {
String redisKey = keyPrefix + offerId;
RedisFuture<List<String>> futureCommand = connectionWrapper.getHashValues(redisKey, getRequiredParams());
futureCommand.thenAccept(valuesList -> {
TrackerScheduler.processT1GenreicPool.execute(ctx, () -> {
Map<String, String> resultMap = reconstructMapValues(valuesList, getRequiredParams(), redisKey, ctx);
T1Offer offerData;
if(!resultMap.isEmpty()) {
offerData = new T1Offer(resultMap);
} else {
offerData = new T1Offer();
}
if(!offerData.isValid()) {
errorHandler.onError(new Exception("Invalid fields in offerData"));
} else {
responseHandler.onResponse(offerData);
}
});
});
}
My best attempt was to send the assertion using the responseHandler method like this:
#Test
public void getOfferData_offerFullData_parseSuccess() throws Exception {
T1ClickOfferDao.instance.getOfferData(null, Integer.parseInt(OFF_ID), resultOffer -> {
Assert.assertEquals("", resultOffer.getActivationDate());
}, error -> {
});
}
but the Test context is finished before the future is evaluated. And even if I Threas.sleep for a second - the assertion does not affect the test result.
How about
#Test
public void getOfferData_offerFullData_parseSuccess() throws Exception {
final String lock = new String("lock");
T1ClickOfferDao.instance.getOfferData(null, Integer.parseInt(OFF_ID), resultOffer -> {
Assert.assertEquals("", resultOffer.getActivationDate());
synchronized(lock){
lock.notifyAll();
}
}, error -> {
});
synchronized(lock){
try{
lock.wait(1000*2);
} catch (InterruptedException ex) {
fail("Timeout");
}
}
}

How to simulate throwing an exception only once in retry with JUnit/Mockito test?

I put a simple retry because the operation can rarely fail. The simplified code is below. The method putObject can accidentally throw an exception, in this case the retry should allow to invoke this method again. Is it possible to write a JUnit test for this?
I know that with Mockito library we can force to throw an Exception invoking a method but how to force this exception to be thrown only once?
public class RetryExample {
Bucket bucket = new Bucket();
static int INTERNAL_EXCEPTION_CODE = 100;
class AException extends RuntimeException {
private static final long serialVersionUID = 1L;
int statusCode;
public int getStatusCode() {
return statusCode;
}
}
class Bucket {
public void putObject(String fileName, byte[] data) throws AException {
System.out.println("PutObject=" + fileName + " data=" + data);
}
}
public void process(String fileName, byte[] data) throws AException {
try {
retryOperation((f, d) -> bucket.putObject(f, d), fileName, data);
} catch (Exception ex) {
throw new AException("Failed to write data", ex);
}
}
private <T, U> void retryOperation(BiConsumer<T, U> biConsumer, T t, U u) {
int retries = 0;
boolean retry = false;
AException lastServiceException = null;
do {
try {
biConsumer.accept(t, u);
} catch (AException e) {
lastServiceException = e;
int statusCode = e.getStatusCode();
if (statusCode == INTERNAL_EXCEPTION_CODE) {
throw e;
} else {
break;
}
}
retries++;
if (retries >= 3) {
retry = false;
}
} while (retry);
if (lastServiceException != null) {
throw lastServiceException;
}
}
Test Class:
public class RetryExampleTest {
...
#Test
public void test() {
RetryExample retryExample = new RetryExample();
String fileName = "TestFile";
byte[] data = simulatedPayload(10000);
try {
retryExample.process(fileName, data);
} catch (Exception e) {
fail("Exception thrown=" + e);
}
}
According to the Mockito documentation you can set different behavior for consecutive method calls.
when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
In case of a void method you can do something similar (Mockito documentation)
doThrow(new RuntimeException())
.doNothing()
.when(mock).doSomething();
I think you can use a global data object to store the times of throw Exceptions, so in the Mockito library invoke the Exception method just taken the global data object to record the times. It would be simple. Just all by your control.

Throwing exception from CompletableFuture

I have the following code:
// How to throw the ServerException?
public void myFunc() throws ServerException{
// Some code
CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
try {
return someObj.someFunc();
} catch(ServerException ex) {
// throw ex; gives an error here.
}
}));
// Some code
}
someFunc() throws a ServerException. I don't want to handle this here but throw the exception from someFunc() to caller of myFunc().
Your code suggests that you are using the result of the asynchronous operation later in the same method, so you’ll have to deal with CompletionException anyway, so one way to deal with it, is
public void myFunc() throws ServerException {
// Some code
CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
try { return someObj.someFunc(); }
catch(ServerException ex) { throw new CompletionException(ex); }
});
// Some code running in parallel to someFunc()
A resultOfA;
try {
resultOfA = a.join();
}
catch(CompletionException ex) {
try {
throw ex.getCause();
}
catch(Error|RuntimeException|ServerException possible) {
throw possible;
}
catch(Throwable impossible) {
throw new AssertionError(impossible);
}
}
// some code using resultOfA
}
All exceptions thrown inside the asynchronous processing of the Supplier will get wrapped into a CompletionException when calling join, except the ServerException we have already wrapped in a CompletionException.
When we re-throw the cause of the CompletionException, we may face unchecked exceptions, i.e. subclasses of Error or RuntimeException, or our custom checked exception ServerException. The code above handles all of them with a multi-catch which will re-throw them. Since the declared return type of getCause() is Throwable, the compiler requires us to handle that type despite we already handled all possible types. The straight-forward solution is to throw this actually impossible throwable wrapped in an AssertionError.
Alternatively, we could use an alternative result future for our custom exception:
public void myFunc() throws ServerException {
// Some code
CompletableFuture<ServerException> exception = new CompletableFuture<>();
CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
try { return someObj.someFunc(); }
catch(ServerException ex) {
exception.complete(ex);
throw new CompletionException(ex);
}
});
// Some code running in parallel to someFunc()
A resultOfA;
try {
resultOfA = a.join();
}
catch(CompletionException ex) {
if(exception.isDone()) throw exception.join();
throw ex;
}
// some code using resultOfA
}
This solution will re-throw all “unexpected” throwables in their wrapped form, but only throw the custom ServerException in its original form passed via the exception future. Note that we have to ensure that a has been completed (like calling join() first), before we query the exception future, to avoid race conditions.
For those looking for other ways on exception handling with completableFuture
Below are several ways for example handling Parsing Error to Integer:
1. Using handle method - which enables you to provide a default value on exception
CompletableFuture correctHandler = CompletableFuture.supplyAsync(() -> "A")
.thenApply(Integer::parseInt)
.handle((result, ex) -> {
if (null != ex) {
ex.printStackTrace();
return 0;
} else {
System.out.println("HANDLING " + result);
return result;
}
})
.thenAcceptAsync(s -> {
System.out.println("CORRECT: " + s);
});
2. Using exceptionally Method - similar to handle but less verbose
CompletableFuture parser = CompletableFuture.supplyAsync(() -> "1")
.thenApply(Integer::parseInt)
.exceptionally(t -> {
t.printStackTrace();
return 0;
}).thenAcceptAsync(s -> System.out.println("CORRECT value: " + s));
3. Using whenComplete Method - using this will stop the method on its tracks and not execute the next thenAcceptAsync
CompletableFuture correctHandler2 = CompletableFuture.supplyAsync(() -> "A")
.thenApply(Integer::parseInt)
.whenComplete((result, ex) -> {
if (null != ex) {
ex.printStackTrace();
}
})
.thenAcceptAsync(s -> {
System.out.println("When Complete: " + s);
});
4. Propagating the exception via completeExceptionally
public static CompletableFuture<Integer> converter(String convertMe) {
CompletableFuture<Integer> future = new CompletableFuture<>();
try {
future.complete(Integer.parseInt(convertMe));
} catch (Exception ex) {
future.completeExceptionally(ex);
}
return future;
}
Even if other's answer is very nice. but I give you another way to throw a checked exception in CompletableFuture.
IF you don't want to invoke a CompletableFuture in another thread, you can use an anonymous class to handle it like this:
CompletableFuture<A> a = new CompletableFuture<A>() {{
try {
complete(someObj.someFunc());
} catch (ServerException ex) {
completeExceptionally(ex);
}
}};
IF you want to invoke a CompletableFuture in another thread, you also can use an anonymous class to handle it, but run method by runAsync:
CompletableFuture<A> a = new CompletableFuture<A>() {{
CompletableFuture.runAsync(() -> {
try {
complete(someObj.someFunc());
} catch (ServerException ex) {
completeExceptionally(ex);
}
});
}};
I think that you should wrap that into a RuntimeException and throw that:
throw new RuntimeException(ex);
Or many be a small utility would help:
static class Wrapper extends RuntimeException {
private Wrapper(Throwable throwable) {
super(throwable);
}
public static Wrapper wrap(Throwable throwable) {
return new Wrapper(throwable);
}
public Throwable unwrap() {
return getCause();
}
}
public static void go() {
CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
try {
throw new Exception("Just because");
} catch (Exception ex) {
throw Wrapper.wrap(ex);
}
});
a.join();
}
And then you could unwrap that..
try {
go();
} catch (Wrapper w) {
throw w.unwrap();
}

Categories