Asynchronous JAX-RS

This chapter covers asynchronous programming support in JAX-RS and some of its potential gotchas

Basics of server side async JAX-RS

JAX-RS 2.0 includes a brand new API for asynchronous processing which includes server as well as client side counterparts. Being asynchronous inherently implies request processing on a different thread than that of the thread which initiated the request

  • From a client perspective, it prevents blocking the request thread since no time is spent waiting for a response from the server.

  • Similarly, asynchronous processing on the server side involves suspension of the original request thread and initiation of request processing on a different thread, thereby freeing up the original server side thread to accept other incoming requests.

The end result of asynchronous execution (if leveraged correctly) is scalability, responsiveness and greater throughput.

Server side async

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

An instance of AsyncResponse can be transparently injected as a method parameter (of a JAX-RS resource class) by annotating it with @Suspended. This instance serves as a callback object to interact with the caller/client and perform operations such as response delivery (post request processing completion), request cancellation, error propagation, and so forth

//Async JAX-RS in action

@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) {
              //ignored. . . don't try this in production!
                  }
                }
            });

        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 its the thread pool taken care of the by the Managed Executor Service in the Java EE container - thanks to Concurrency Utilties in Java EE 7)

Things to watch out for

Although the calling (request) thread is released, the underlying I/O thread still blocks until the processing in background thread continues.In the above example, the caller would have to wait 5 seconds since that's the delay we have purposefully introduced within the code. In simple words, the client (e.g. browser executing a HTTP GET) keeps waiting until the business logic execution is finished by calling the resume method of the injected AsyncResponse object

Timeouts to the rescue

One can specify a time out period after which the client gets back a HTTP 503 Service Unavailable response (default convention)

//Basic async timeout config

@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 time outs

The default behaviour (HTTP 503 on time out) 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 time out can prove useful. The AsyncResponse API makes this possible via the notion of a time out 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)

//fine grained timeout config

@GET
public void async(@Suspended AsyncResponse ar) {
  ar.setTimeout(3, TimeUnit.SECONDS);
  ar.setTimeoutHandler(new TimeoutHandler() {
    @Override
      public void handleTimeout(AsyncResponse asyncResponse) {
          //sending HTTP 202 (Accepted)
          asyncResponse.resume(Response
          .accepted(UUID.randomUUID().toString())
          .build()); 
      }
});

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

//Client side async

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);

AsyncInvoker interface supports asynchronous invocation with dedicated methods (get(), post(), put() and so on) for standard HTTP actions: GET, PUT, POST, DELETE, HEAD, TRACE, and OPTIONS.

Response handling via Callbacks and Futures

Once registered, an implementation of the InvocationCallback will automatically be executed once the asynchronous request is processed. It provides the ability to account for both successful and exceptional scenarios. In the event a Future object is obtained and no callback has been registered, manually poll it in order to interact with the response. isDone, get, cancel are some of the methods that can be invoked

//Client side async callback

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

Invocation.Builder reqBuilder = target.request();
Invocation invocation = reqBuilder.buildGet();

Future<Response> future2 = 
  invocation.submit(
    new InvocationCallback<Customer>(){
    public void completed(Customer cust){
        System.out.println(
          "Customer ID:" + cust.getID());
    }
     public void failed(Throwable t){
        System.out.println(
          "Unable to fetch Cust details: " + 
          t.getMessage());
      }
});

Client side gotchas

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. One 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`) .

Do not remain under the impression that your client thread will return immediately just because the server API is async

//Client side async gotcha!

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();
    //this will block until server responds or triggers out (even if its aysnc)
    Response response = reqBuilder.get(); 

    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](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#get()) method (of Future) blocks. So use it with care and at the correct point in your application logic

Last updated