Vamsi
Vamsi

Reputation: 149

How to ignore a failed CompletableFuture call when multiple Async calls are made in Java?

I want to consume three REST service parallel calls in Java and my code is as below:

private CompletableFuture < EmployeesListResponse > makeAsyncCall(Request request) {
 return CompletableFuture.supplyAsync(
  () -> {
   try {
    LOGGER.warn("- - - - - Employees Service async call - - - - -");
    return serviceObj.findServiceImpl(request);
   } catch (Exception e) {
    LOGGER.warn("service async call failed...", e);
   }
   return null;
  }, asyncExecutor).handle((res, ex) -> {
  LOGGER.warn("Exceptionally...", ex.toString(), res.toString());
  return new EmployeesListResponse();
 });
}

CompletableFuture < EmployeesListResponse > asyncFirstCall = makeAsyncCall(request);
CompletableFuture < EmployeesListResponse > asyncSecondCall = makeAsyncCall(request);
CompletableFuture < EmployeesListResponse > asyncThirdCall = makeAsyncCall(request);

CompletableFuture.allOf(asyncFirstCall, asyncSecondCall, asyncThirdCall).join();

In the above code, I am making three calls and joining them using CompletableFuture.allOf().join(). This code is working perfectly fine when the service response is 200 OK for all the three calls.

If one call fails(500 Internal Server Error or 404 Not Found) and other two service calls are 200 OK, then the code is throwing exception and the entire API response is getting failed with an exception. In this case, i want to ignore one service call with exception and return success response from the other two calls.

How to handle to ignore an exception in this scenario ?

Upvotes: 3

Views: 4528

Answers (3)

karthick M
karthick M

Reputation: 302

If you want to ignore, try like below

CompletableFuture<Void> allOf = CompletableFuture.allOf(asyncFirstCall,asyncSecondCall);
allOf.whenComplete((aVoid, throwable) -> {
    allOf.join();
});

Upvotes: 0

Mr. Jain
Mr. Jain

Reputation: 146

You don't need to handle the exception in try catch block as you are already handling handle() method. No matter you get the exception or not but this handle() method will execute every time. you just need to check if is there any exception if yes send default response as you want.

private CompletableFuture < EmployeesListResponse > makeAsyncCall(Request request) {
     return CompletableFuture.supplyAsync(
      () -> {
        LOGGER.warn("- - - - - Employees Service async call - - - - -");
        return serviceObj.findServiceImpl(request);
      }, asyncExecutor).handle((res, ex) -> {
          if (ex != null) {
              LOGGER.warn("Exceptionally...", ex);
              return "what ever default you want to return";
   //         or return new EmployeesListResponse();
            }
            return res;
     });
    }

Upvotes: 0

MikeFHay
MikeFHay

Reputation: 9013

So you're trying to wait for all 3 futures to complete before finishing, but the allOf future returns immediately when a single one fails. Instead you can explicitly wait on each:

List<CompletableFuture<String>> allFutures = Arrays.asList(asyncFirstCall, asyncSecondCall,
        asyncThirdCall);
// await completion of all futures
allFutures.forEach(future -> {
    try {
        future.join();
    } catch (CompletionException ex) {
        // handled below.
    }
});

if (allFutures.stream().filter(CompletableFuture::isCompletedExceptionally).count() > 2) {
    throw new RuntimeException("Multiple failures");
}

// else continue with your business logic...

Upvotes: 3

Related Questions