4

I am creating a async rest call using spring

@GetMapping(path = "/testingAsync")
public String value() throws ExecutionException, InterruptedException, TimeoutException {
    AsyncRestTemplate restTemplate = new AsyncRestTemplate();
    String baseUrl = "https://api.github.com/users/XXX";
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    String value = "";

    HttpEntity entity = new HttpEntity("parameters", requestHeaders);
    ListenableFuture<ResponseEntity<User>> futureEntity = restTemplate.getForEntity(baseUrl, User.class);

    futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<User>>() {
        @Override
        public void onSuccess(ResponseEntity<User> result) {
            System.out.println(result.getBody().getName());
            // instead of this how can i return the value to the user ?
        }

        @Override
        public void onFailure(Throwable ex) {

        }
    });

    return "DONE"; // instead of done i want to return value to the user comming from the rest call 
}

And is there any way i can convert ListenableFuture to use CompletableFuture that is used in java 8 ?

2
  • 1
    Just return the futureEntity instead of DONE, Spring MVC will do the rest. Or create a DefferedResult and set the value of that result in the onSuccess and return the Deferredresult from your method.
    – M. Deinum
    Commented Jun 19, 2017 at 12:48
  • with deffered result its working but not with futureentity, is there any way we can use completable future instead of listenablefuture
    – Rahul
    Commented Jun 19, 2017 at 13:00

2 Answers 2

7

There are basically 2 things you can do.

  1. Remove the ListenableFutureCallback and simply return the ListenableFuture
  2. Create a DeferredResult and set the value of that in a ListenableFutureCallback.

Returning a ListenableFuture

@GetMapping(path = "/testingAsync")
public ListenableFuture<ResponseEntity<User>> value() throws ExecutionException, InterruptedException, TimeoutException {
    AsyncRestTemplate restTemplate = new AsyncRestTemplate();
    String baseUrl = "https://api.github.com/users/XXX";
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    String value = "";

    HttpEntity entity = new HttpEntity("parameters", requestHeaders);
    return restTemplate.getForEntity(baseUrl, User.class);
}

Spring MVC will add a ListenableFutureCallback itself to fill a DeferredResult and you will get a User eventually.

Using a DeferredResult

If you want more control on what to return you can use a DeferredResult and set the value yourself.

@GetMapping(path = "/testingAsync")
public DeferredResult<String> value() throws ExecutionException, InterruptedException, TimeoutException {
    AsyncRestTemplate restTemplate = new AsyncRestTemplate();
    String baseUrl = "https://api.github.com/users/XXX";
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    String value = "";

    HttpEntity entity = new HttpEntity("parameters", requestHeaders);
    final DeferredResult<String> result = new DeferredResult<>();
    ListenableFuture<ResponseEntity<User>> futureEntity = restTemplate.getForEntity(baseUrl, User.class);

    futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<User>>() {
        @Override
        public void onSuccess(ResponseEntity<User> result) {
            System.out.println(result.getBody().getName());
            result.setResult(result.getBody().getName());
        }

        @Override
        public void onFailure(Throwable ex) {
            result.setErrorResult(ex.getMessage());
        }
    });

    return result;
}
5
  • when i am using the first method its getting stuck and not returning the value in postman the second one works ? can you please help on that . One more trouble will this be good in terms of performance?
    – Rahul
    Commented Jun 19, 2017 at 13:05
  • Generally you won't gain performance but you can do more with less resources. If you would need to do multiple calls to heavy resources, you could gain performance when those could be done in parallel.
    – M. Deinum
    Commented Jun 19, 2017 at 13:23
  • And why is the first way not working without using deffered?How can we call multiple rest ends in the same can you just give a tiny bit of hint.
    – Rahul
    Commented Jun 19, 2017 at 13:36
  • Not in comments or this question as that is a different questions. Not sure why the first isn't working, should work afaik (assuming you are on Spring 4.3 that is).
    – M. Deinum
    Commented Jun 19, 2017 at 13:45
  • I am using spring boot
    – Rahul
    Commented Jun 19, 2017 at 14:00
0

I don't know too much about async calls in Spring but I would imagine that you could return the text that you want through the ResponseBody

It would look like this:

@GetMapping(path = "/testingAsync")
@ResponseBody
public String value() throws ExecutionException, InterruptedException, TimeoutException {
...
...
     @Override
     public void onSuccess(ResponseEntity<User> result) {
         return result.getBody().getName();
     }
...
}

Sorry if this isn't what you are asking about.

2
  • That won't work, due to the async nature of the call.
    – M. Deinum
    Commented Jun 19, 2017 at 12:53
  • Ah, I think I see the problem. Not sure how to solve this but I am eager to understand how to work with asynchronous tasks. If I were to think of hacky ways to go about this, I'd guess that you could delegate the responsibility of returning a value to a different function? i.e., maybe direct the onSuccess to another mapping called "/returnAsync" which then can return the value that you want?
    – Jack F.
    Commented Jun 19, 2017 at 13:02

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.