Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Asynchronous JAX-RS: Basics and Gotchas

DZone's Guide to

Asynchronous JAX-RS: Basics and Gotchas

This blog post covers asynchronous programming support in JAX-RS and some of its potential gotchas, along with a sample (Maven-based) Java EE project on GitHub.

· Integration Zone
Free Resource

Discover how Microservices are a type of software architecture where large applications are made up of small, self-contained units working together through APIs that are not dependent on a specific language. Brought to you in partnership with AppDynamics.

This blog post covers:

Man, I tend to blog a lot about JAX-RS and REST !

Basics of Server Side Async JAX-RS

On the server side, asynchronous behaviour is driven by

  • @Suspended: annotation which instructs the container to inject an instance of AsyncResponse and invoke the method asynchronously
  • AsyncResponse: bridge between the application logic and the client request
@Path("async")
@Stateless
public class AsyncResource {

    @Resource
    ManagedExecutorService mes;

    @GET
    public void async(@Suspended AsyncResponse ar) {

        String initialThread = Thread.currentThread().getName();
        System.out.println("Thread: "+ initialThread + " in action...");

        mes.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        String processingThread = Thread.currentThread().getName();
                        System.out.println("Processing thread: " + processingThread);

                        Thread.sleep(5000);
                        String respBody = "Process initated in " + initialThread + " and finished in " + processingThread;
                        ar.resume(Response.ok(respBody).build());                    

                    } catch (InterruptedException ex) {
                        Logger.getLogger(AsyncResource.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            });

        System.out.println(initialThread + " freed ...");
    }
}

Since we have clearly expressed our asynchronous requirements, the container will ensure that:

  • the calling thread is released
  • the actual business logic is executed in a different thread (in this case, it's the thread pool taken care of by the Managed Executor Service in the Java EE container–thanks to Concurrency Utilties in Java EE 7)

Things to Watch Out for ... a.k.a Gotchas

  • Although the calling (request) thread is released, the underlying I/O thread still blocks until the processing in background thread continues. In simple words, the client (e.g. browser executing an HTTP GET) keeps waiting until the business logic execution is finished by calling the resume method of the injected AsyncResponse object (remember, it’s the bridge between the application and the client). In the above example, the client would have to wait 5 seconds since that’s the delay we have purposefully introduced within the code
  • Timeouts to the rescue: one can specify a timeout period after which the client gets back an HTTP 503 Service Unavailable response (default convention)
@Path("async")
@Stateless
public class AsyncResource {

    @Resource
    ManagedExecutorService mes;

    @GET
    public void async(@Suspended AsyncResponse ar) {
        ar.setTimeout(3, TimeUnit.SECONDS); //setting the time out to 3 seconds

        String initialThread = Thread.currentThread().getName();
        ......
    }
}    

Fine-grained Timeouts

The default behaviour (HTTP 503 on a timeout) might not be suitable for all use cases. For example, you might want to implement a solution where a tracking identifier needs to be sent to the client (for future) if the actual processing does not finish in due time (before timeout triggers). Having the ability to send a custom HTTP response on timeout can prove useful. The AsyncResponse API makes this possible via the notion of a timeout handler. You can do this by:

  • a direct HTTP response (see below example)
  • via an exception (by passing an instance of Throwable to AsyncResponse#resume)
@GET
public void async(@Suspended AsyncResponse ar) {
  ar.setTimeout(3, TimeUnit.SECONDS);
  ar.setTimeoutHandler(new TimeoutHandler() {
    @Override
      public void handleTimeout(AsyncResponse asyncResponse) {
          asyncResponse.resume(Response.accepted(UUID.randomUUID().toString()).build()); //sending HTTP 202 (Accepted)
      }
  });
  ......

Client Side Async Using the JAX-RS Client API

Using asynchronous behaviour on the client side is pretty easy. All you need to do is obtain an instance of AsyncInvoker by calling async on the Invocation.Builder

public Response test() throws Exception{
    Client client = ClientBuilder.newBuilder().build();
    WebTarget target = client.target("https://api.github.com/search/users?q=abhirockzz");

    Invocation.Builder reqBuilder = target.request();
    AsyncInvoker asyncInvoker = reqBuilder.async();
    Future<Response> futureResp = asyncInvoker.get();

    Response response = futureResp.get(); //blocks until client responds or times out

    String responseBody = response.readEntity(String.class);
    return Response.status(response.getStatus()).entity(responseBody).build();
}

Above Gotchas Applicable Here

As you might have already observed, async behaviour in (server side) JAX-RS is not the same as in other typical async APIs, i.e. the client is still blocked.

  • You should use the async method (as demonstrated above) to call a server side REST API in an async manner even if the server REST API itself is asynchronous in nature (implemented using @Suspended AsyncResponse) .

I repeat: do not be under the impression that just because the server API is async, your client thread will return immediately.

public Response test() throws Exception{
    Client client = ClientBuilder.newBuilder().build();
    WebTarget target = client.target("http://localhost:8080/jaxrs-async-service/async");

    Invocation.Builder reqBuilder = target.request();
    Response response = reqBuilder.get(); //this will block until server responds or triggers out (even if its aysnc)

    String responseBody = response.readEntity(String.class);
    return Response.status(response.getStatus()).entity(responseBody).build();
}
  • The call to async returns an instance of Future object–and, you might already know, the get method (of Future) blocks. So, use it with care and at the correct point in your application logic.

Further Reading

That’s all. Be async. Be safe!

Discover the six challenges and best practices in managing microservice performance, brought to you in partnership with AppDynamics.

Topics:
java ,jax-rs ,asynchronous

Published at DZone with permission of Abhishek Gupta, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}