Home > Net >  CompletableFuture: how to combine two asynchronous requests into one response
CompletableFuture: how to combine two asynchronous requests into one response

Time:05-28

The first request is sent asynchronously, then the id value is taken from the response to the first request and used in the second asynchronous request. And then both responses to request 1 and request 2 are combined. The example below works, but is not asynchronous because .get() is used. Is there a way to do this asynchronously? The process in short - everything should happen asynchronously:

  1. Send POST request 1
  2. Use the id value from response 1 for request 2
  3. Send POST request 2
  4. Combine response 1 and response 2 to the final response of the REST controller

This body is sent to the REST controller endpoint "/combine" via POST method:

{ "userid": 1, "id": 2, "title": "3", "body": "4" }

@RestController
public class CombinationController {
    @Autowired
    CombinationService combinationService;

    @PostMapping("/combine")
    public CompletableFuture<CombinationBothResponses> combine(@RequestBody RequestBodyOne requestBodyOne) {
        return combinationService.combine(requestBodyOne);
    }
}
@Service
public class CombinationService {
    private final Jsonb jsonb = JsonbBuilder.create();
    private final HttpClient httpClient = HttpClient.newBuilder().build();

    public CompletableFuture<CombinationBothResponses> combine(RequestBodyOne requestBodyOne) {
        // 1. Send POST request 1
        HttpRequest httpRequestOne =
                HttpRequest.newBuilder(URI.create("https://jsonplaceholder.typicode.com/posts"))
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .POST(HttpRequest.BodyPublishers.ofString(jsonb.toJson(requestBodyOne)))
                        .build();

        return httpClient
                .sendAsync(httpRequestOne, HttpResponse.BodyHandlers.ofString())
                .thenApply(
                        httpResponse -> {
                            if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
                                CombinationBothResponses combinationBothResponses =
                                        jsonb.fromJson(httpResponse.body(), CombinationBothResponses.class);
                                // 2. Use one value from response 1 for request 2
                                int valueToBeUsedInRequestBody2 = combinationBothResponses.getId();
                                // 3. Send POST request 2
                                CompletableFuture<CombinationBothResponses> completableFuture2 =
                                        sendSecondPostRequest(valueToBeUsedInRequestBody2);
                                // 4. Combine response 1 and response 2 to the final response of REST controller
                                try {
                                    CombinationBothResponses responseBodyRequestTwo =
                                            completableFuture2.get(); // Not asynchronous
                                    combinationBothResponses.setSuccess(responseBodyRequestTwo.getSuccess());
                                    return combinationBothResponses;

                                } catch (InterruptedException | ExecutionException e) {
                                    e.printStackTrace();
                                }
                            }
                            throw new RuntimeException();
                        });
    }

    private CompletableFuture<CombinationBothResponses> sendSecondPostRequest(
            int valueToBeUsedInRequestBody2) {
        RequestBodyTwo requestBodyTwo = new RequestBodyTwo(valueToBeUsedInRequestBody2, "request 2");
        HttpRequest httpRequest =
                HttpRequest.newBuilder(URI.create("https://reqbin.com/echo/post/json"))
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .POST(HttpRequest.BodyPublishers.ofString(jsonb.toJson(requestBodyTwo)))
                        .build();

        return httpClient
                .sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
                .thenApply(
                        httpResponse -> {
                            if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
                                CombinationBothResponses responseBodyRequestTwo =
                                        jsonb.fromJson(httpResponse.body(), CombinationBothResponses.class);
                                return responseBodyRequestTwo;
                            }
                            throw new RuntimeException();
                        });
    }
}
@Data
@NoArgsConstructor
public class RequestBodyOne {
    private int userId;
    private int id;
    private String title;
    private String body;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RequestBodyTwo {
    private int id;
    private String key;
}
@Data
@NoArgsConstructor
public class CombinationBothResponses {
    private int userId;
    private int id;
    private String title;
    private String body;

    private String success;

}

Response to request 1:

{ "userId": 0, "id": 101, "title": "3", "body": "4" }

Response to request 2:

{"success":"true"}

Combined responses; response of REST controller:

{ "userId": 0, "id": 101, "title": "3", "body": "4", "success": "true" }

CodePudding user response:

return httpClient
     .sendAsync(httpRequestOne, HttpResponse.BodyHandlers.ofString())
     .thenCompose(httpResponse -> {
         if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
             final CombinationBothResponses combinationBothResponses = jsonb.fromJson(httpResponse.body(), CombinationBothResponses.class);
             // 2. Use one value from response 1 for request 2
             int valueToBeUsedInRequestBody2 = combinationBothResponses.getId();
             // 3. Send POST request 2
             return sendSecondPostRequest(valueToBeUsedInRequestBody2)
                 .thenApply(responseBodyRequestTwo -> {
                     // 4. Combine response 1 and response 2 to the final response of REST controller
                     combinationBothResponses.setSuccess(responseBodyRequestTwo.getSuccess());
                     return combinationBothResponses;
                 });
         }
         return CompletableFuture.failedFuture(new RuntimeException());
     });
  • Related