b.lopes
b.lopes

Reputation: 475

Async call of a FeignClient Springboot with CompletableFuture

I want to call async rest endpoints with Feign client and have done the below changes.

When calling it the CompletableFuture.get() doesn't complete.

The while keeps looping...

while(!combinedFuture.isDone()) { log.info("useraccount - waiting for combinedFuture 2: " + request.toString()); }

Interface to call the API:

@FeignClient(value = "clientUser", url = "http://localhost:8898/springboot", fallback = UserFallback.class)
public interface User {

@RequestMapping(method = RequestMethod.GET, value = "/user/", produces = "application/json")
@Async
CompletableFuture<UserInfo> findUserInfo(@RequestHeader(value = "Authorization", required = true) String authorizationHeader);
}

Controller method:

@PostMapping(value = "/springboot/useraccount/", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> userAccount(@requestbody @Valid AuthRequest request) {
log.info("useraccount - request received with request body: " + request.toString());
try {

   if (Strings.isBlank(request.getUsername()) || Strings.isBlank(request.getPassword())) {
       throw new BadRequestException("invalid username or password");
   }

   String token = authorisationService.obtainAuthToken(request.getUsername(), request.getPassword());

   CompletableFuture<UserInfo> userInfo = clientUser.findUserInfo(token);

   CompletableFuture<UserAccountInfo> userAccountInfo = clientAccount.findAccountInfo(token);

   CompletableFuture<Void> combinedFuture
           = CompletableFuture.allOf(userInfo, userAccountInfo);

   while(!combinedFuture.isDone()) {
       log.info("useraccount - waiting for combinedFuture 2: " + request.toString());
   }
   Optional<UserAccountResponse> userAccountResponse = userAccountService.getAccountInfo(
           userAccountInfo.get(), userInfo.get()
   );


   if (userAccountResponse.isEmpty()) {
       throw new BadCredentialsException("Bad Credentials");
   }

   return ResponseEntity.ok().body(userAccountResponse);
   } catch (BadCredentialsException | UnAuthorizedException ex) {
   return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
   } catch (BadRequestException ex) {
   return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
   } catch (ExecutionException e) {
   return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
   } catch (InterruptedException e) {
   return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
   }
}

(update) Have changed a bit to use CompletableFuture.supplyAsync

but now the object is always null...

using CompletableFuture.supplyAsync

@Service
public class AccountService {

@Autowired
Account accountClient;

@Async
public Optional<UserAccountInfo> getAccountInfo(String token) {
    return Optional.of(accountClient.findAccountInfo(token));
}

}

Upvotes: 2

Views: 8549

Answers (1)

b.lopes
b.lopes

Reputation: 475

Managed to solve it like this:

 @Async
public CompletableFuture<UserAccountInfo> getAccountInfo(String token)   {
    try {
        System.out.println(
                "Current Thread account Name: "
                        + Thread.currentThread().getName());
        Thread.currentThread().sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return    CompletableFuture.completedFuture(accountClient.findAccountInfo(token).getBody());
}

The same for the userInfo service. Then on the controller:

CompletableFuture<UserAccountInfo> userAccountInfo = accountService.getAccountInfo(token);
       CompletableFuture<UserInfo> userInfo = userService.getUserInfo(token);

       Optional<UserAccountResponse> userAccountResponse = userAccountService.getAccountInfo(
               userAccountInfo.get(),userInfo.get()
       );

So that means both services will start running each in a new thread while the main thread continues ran until find the first .get().

By doing it like that the maximum waiting time to finish is the time of the thread that takes more time and not the sum of both as it happens if it is a synchronous.

Thanks!

Upvotes: 1

Related Questions