I have a program that makes HTTP requests, where I might need to try several different servers to get a successful response. The HTTP client is async, so I get CompletableFuture results when making requests.
I can easily try the "next" server when I get a bad response status code, for example:
return httpClient.sendAsync(request,
HttpResponse.BodyHandlers.ofByteArray()
).thenCompose(response -> {
if (response.statusCode() == 200) {
return completedStage(...); // all ok, done!
}
// try the next server
return callThisMethodAgain(...);
);
This works because thenCompose expects the lambda it's given to return another CompletableFuture so I can chain asynchronous computations.
However, if the server is not available, the HTTP client will throw an Exception and my lambda won't execute and the async chain terminates with that Exception.
I can see there are methods like handle and handleAsync, but they don't let me return yet another CompletableFuture, as thenCompose does. They are kind of analogous to thenApply (i.e. like stream's map rather than flatMap), so it seems to me CompletableFuture is missing the method that would be analogous to flatMap where I want to handle both success and failures from the previous async action in the chain?
Is that correct? Is there some workaround I am missing?