1

I am using Java 17. I am trying to get the response from a message service and create a map object. Then send it to the caller method. I am using java.net.http.

I am willing to use a callback so that I could not use join or get to delay the main thread. But at my callback, the message shown unexpected return value.

My attempts are as below:

    LinkedHashMap<String, Object> retVal = new LinkedHashMap<>();

HttpRequest request = HttpRequest.newBuilder(URI.create(url))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(params))
                .build();               
        
HttpClient.newHttpClient()
                .sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(responseBody -> {
                    try {
                        LinkedHashMap<String, Object> responseMap = new ObjectMapper().readValue(responseBody.body(), new TypeReference<>() {});

                        ArrayList<LinkedHashMap<String, String>> resultList = (ArrayList<LinkedHashMap<String, String>>) responseMap.get("result");
                        HashMap<String, String> resultMap = resultList.get(0);
                        retVal.put("status", resultMap.get("status"));
                        retVal.put("message", resultMap.get("message"));
                    } catch (JsonProcessingException e) {
                        retVal.put("status", "FAILED");
                        retVal.put("message", e.getMessage());
                        e.printStackTrace();
                    }
                    return retVal;
                })
                .exceptionally(ex -> {
                    retVal.put("status", "FAILED");
                    retVal.put("message", ex.getMessage());
                    System.out.println("Exception occurred: " + ex.getMessage());
                    return retVal;
                })
                .thenAccept(callback);  
                
// Define a callback function
        Consumer<LinkedHashMap<String, Object>> callback = responseBody -> {
            // Send the response back to the caller here
            return responseBody;
        };  

How can I achieve this?

7
  • 1. if you want to return something then instead of thenAccept() use thenApply(). If you use thenAccept() the only possible value that you can return is null - which is the only value compatible with Void. 2. in your first lambda, where the parameter is an HttpResponse<String>, you should probably look at the HTTP status code before mapping the body to JSon. Commented Mar 7, 2023 at 17:51
  • This looks like a good question, but prior to my edit it rather sounded like a request for free work. It is worth being aware that Stack Overflow gets hundreds of requests for volunteer labour every day, and most of those are downvoted and/or closed (quite rightly). I recommend differentiating your own questions from those - make it clear that you know the work is yours to complete. Commented Mar 7, 2023 at 20:01
  • @daniel if I use only thenApply() then I have to use join other wise it shows error. In first lambda not looking for status code because if it fails Then I have to save response into a table as well Commented Mar 12, 2023 at 5:13
  • I guess this is your IDE complaining that you ignore the result (not a compiler error). Then just return null. You're ignoring the returned completable feature anyway, so why not use thenAccept() and return null? Commented Mar 13, 2023 at 14:11
  • 1
    If you don't want to return null, change your code to: CompletableFuture<Map<String, Object>> result = HttpClient.newClient().sendAsync(...).thenApply(...).exceptionally(...).thenApply(body -> callback(body)); where callback(body) is a Function<Map<String,Object>,Map<String,Object>>. Commented Mar 14, 2023 at 15:18

1 Answer 1

0

Method:

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)

can only accept a Consumer object. A Consumer object does not return anything, it only consumes an action so it could for instance look like this:

Consumer<LinkedHashMap<String, Object>> callback = System.out::println;

The best would be to use your consumer method and do there whatever you would like to do with the retVal variable. Instead of returning you have to do your logic inside the callback method. For instance:

Consumer<LinkedHashMap<String, Object>> callback = linkedMap -> {
    if (linkedMap != null) {
        linkedMap.keySet().forEach(k -> doSomethingWithEveryKey(k));
    }
};

If you would like to create a fully asynchronous and non-blocking application you can use Spring Webflux, where your method can look like:

@GetMapping("/firstCall")
public Mono<String> getMessage() {
    return this.client.get().uri("/secondCall").accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(String.class)
            .map(this::mapResponse);
}

private String mapResponse(String input) {
    return "modified response";
}
Sign up to request clarification or add additional context in comments.

5 Comments

But user is waiting for a response that was it success or not. That's why I need to return
I can do it by using get() or join() but that will stop the main thread. So performance will be compromised
So I assume you are making this call from inside your own controller method (which is handling a REST call)? In your current implementation, the "main" thread is not waiting for the result, and will simply continue without wating and will "lose" the result from the http POST call. I think this sort of functionality which you would like to achieve can be done via Spring Webflux.
If you will post the whole enclosing method I will try to propose you a solution based on spring Webflux
I will try Webflux later as you suggest

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.