Here I am going to give an example on how Asynchronous REST webservice with Completion Callback.

Introduction

The most important concept in REST is resources, which are identified by global IDs — typically using URIs. Client applications use HTTP methods (GET/ POST/ PUT/ DELETE) to manipulate the resource or collection of resources. A RESTful Web service is implemented using HTTP and the principles of REST. Typically, a RESTful Web service should define the following aspects:

The base/root URI for the Web service such as http://<host>/<appcontext/contextpath>/<url pattern>/<resources>.
The MIME type of the response data supported, which are JSON/XML/TEXT/HTML etc.
The set of operations supported by the service. (for example, POST, GET, PUT or DELETE).

Methods

HTTP methods are mapped to CRUD (create, read, update and delete) actions for a resource. Although you can make slight modifications such as making the PUT method to be create or update, the basic patterns are listed as follows.

HTTP GET: Get/List/Retrieve an individual resource or a collection of resources.
HTTP POST: Create a new resource or resources.
HTTP PUT: Update an existing resource or collection of resources.
HTTP DELETE: Delete a resource or collection of resources.

Prerequisites

Eclipse, JDK 1.6

For more information on Asynchronous Service please go through https://jersey.java.net/documentation/latest/async.html

Asynchronous Server API

By default client connection of a request is processed in a synchronous mode in server in a single I/O container thread. After processing the client request, the thread returns to the I/O container and I/O container safely assumes that the request process is finished. Thus all resources associated with the client connection are released. This kind of synchronous mechanism is sufficient for requests which take relatively short execution time. But for requests which take relatively longer execution time, the association between a request processing thread and client connection is broken. Thus server-side asynchronous processing model should be used to facilitate explicitly suspend, resume and close the client connections.

Now I will go to the coding part and below steps to be followed to create an asynchronous REST servive. We will use grizlly web server for running the service.

Step 1. Create maven webapp project in Eclipse with the below information

Group Id: in.webtuts
Artifact Id: rest

Step 2. Modify the pom.xml file to use jar libraries

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>in.webtuts</groupId>
    <artifactId>rest</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>rest Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <jersey.version>2.6</jersey.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.grizzly</groupId>
            <artifactId>grizzly-http-server</artifactId>
            <version>2.3.11</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.bundles.repackaged</groupId>
            <artifactId>jersey-guava</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-grizzly2-http</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
            <artifactId>jersey-test-framework-provider-inmemory</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>rest</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

 

Step 3. Modify web.xml file to use the jersey servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>rest</display-name>
    <servlet>
        <servlet-name>REST</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>in.webtuts.rest.resources</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Map /rest/* to Jersey -->
    <servlet-mapping>
        <servlet-name>REST</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

 

Step 4. Create a REST resource class as shown below

package in.webtuts.rest.resources;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Response;

@Path("/resource")
public class AsyncResource {

  private static int numberOfSuccessResponses = 0;
  private static int numberOfFailures = 0;
  private static Throwable lastException = null;

  @GET
  @Path("/asyncCompCallback")
  public void asyncGetCompletionCallback(@Suspended final AsyncResponse asyncResponse) {
    asyncResponse.register(new CompletionCallback() {
      @Override
      public void onComplete(Throwable throwable) {
        if (throwable == null) {
          // no throwable - the processing ended successfully
          // (response already written to the client)
          numberOfSuccessResponses++;
        } else {
          numberOfFailures++;
          lastException = throwable;
        }
      }
    });

    new Thread(new Runnable() {
      @Override
      public void run() {
        String result = veryExpensiveOperation();
        asyncResponse.resume(result);
      }

      private String veryExpensiveOperation() {
        return "Very Expensive Operation with Completion Callback";
      }
    }).start();
  }

}

 

Step 5. Create JUnit test class for testing the service

package in.webtuts.rest.resources;

import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.ws.rs.client.AsyncInvoker;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class AsyncResourceTest {

  private HttpServer httpServer;
  private WebTarget webTarget;
  private static final URI baseUri = URI.create("http://localhost:9090/rest/");

  @Before
  public void setup() throws Exception {
    //create ResourceConfig from Resource class
    ResourceConfig rc = new ResourceConfig(AsyncResource.class);

    //create the Grizzly server instance
    httpServer = GrizzlyHttpServerFactory.createHttpServer(baseUri, rc);
    //start the server
    httpServer.start();

    //configure client with the base URI path
    Client client = ClientBuilder.newClient();
    webTarget = client.target(baseUri);
  }

  @After
  public void tearDown() throws Exception {
    //if you want to stop the server from the input through keyboard then uncomment below two lines
    // System.out.println(String
    // .format("Application started.%nHit enter to stop it..."));
    // System.in.read();

    //stop the server
    httpServer.shutdown();
  }

  @Test
  public void testAsyncGetCompletionCallback() throws InterruptedException, ExecutionException {
    final AsyncInvoker asyncInvoker = webTarget.path("resource/asyncCompCallback").request().async();
    final Future<Response> responseFuture = asyncInvoker.get();
    System.out.println("Request is being processed asynchronously.");
    final Response response = responseFuture.get();
    // get() waits for the response to be ready
    System.out.println("Response received : " + response);
    System.out.println("Response from GET method : " + response.readEntity(String.class));
  }

}

 

Step 6. Run the JUnit test class.

Console output

Nov 19, 2014 11:34:25 AM org.glassfish.jersey.server.ApplicationHandler initialize
INFO: Initiating Jersey application, version Jersey: 2.6 2014-02-18 21:52:53...
Nov 19, 2014 11:34:26 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9090]
Nov 19, 2014 11:34:26 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Request is being processed asynchronously.
Response received : InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9090/rest/resource/asyncCompCallback, status=200, reason=OK}}
Response from GET method : Very Expensive Operation with Completion Callback
Nov 19, 2014 11:34:26 AM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [localhost:9090]
Nov 19, 2014 11:34:26 AM org.glassfish.grizzly.nio.GracefulShutdownRunner run
WARNING: GRIZZLY0032: Primary shutdown thread interrupted.  Forcing transport termination.

 

Or

Step 5. Create JUnit test class for testing the service

package in.webtuts.rest.resources;

import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.ws.rs.client.AsyncInvoker;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class AsyncResourceTest {

  private HttpServer httpServer;
  private WebTarget webTarget;
  private static final URI baseUri = URI.create("http://localhost:9090/rest/");

  @Before
  public void setup() throws Exception {
    //create ResourceConfig from Resource class
    ResourceConfig rc = new ResourceConfig(AsyncResource.class);

    //create the Grizzly server instance
    httpServer = GrizzlyHttpServerFactory.createHttpServer(baseUri, rc);
    //start the server
    httpServer.start();

    //configure client with the base URI path
    Client client = ClientBuilder.newClient();
    webTarget = client.target(baseUri);
  }

  @After
  public void tearDown() throws Exception {
    //if you want to stop the server from the input through keyboard then uncomment below two lines
    // System.out.println(String
    // .format("Application started.%nHit enter to stop it..."));
    // System.in.read();

    //stop the server
    httpServer.shutdown();
  }

  @Test
  public void testAsyncGetCompletionCallback()throws InterruptedException,ExecutionException{
    final AsyncInvoker asyncInvoker = webTarget.path("resource/asyncCompCallback").request().async();
    asyncInvoker.get(new InvocationCallback<Response>() {
      @Override
      public void completed(Response response) {
        System.out.println("Request is being processed asynchronously with Callback.");
        // get() waits for the response to be ready
        System.out.println("Response received : " + response);
        System.out.println("Response from GET method : " + response.readEntity(String.class));
      }
      @Override
      public void failed(Throwable throwable) {
        System.out.println("Invocation failed.");
        throwable.printStackTrace();
      }});
  }

}

 

Step 6. Run the JUnit test class.

Console output

Nov 24, 2014 10:50:58 AM org.glassfish.jersey.server.ApplicationHandler initialize
INFO: Initiating Jersey application, version Jersey: 2.6 2014-02-18 21:52:53...
Nov 24, 2014 10:50:59 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9090]
Nov 24, 2014 10:50:59 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Request is being processed asynchronously with Callback.
Response received : InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9090/rest/resource/asyncCompCallback, status=200, reason=OK}}
Response from GET method : Very Expensive Operation with Completion Callback

 

That’s all. Thanks for your reading.

Tags:

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on Roy Tutorials | TwitterFacebook Google PlusLinkedin | Reddit | Email Me

Leave a Reply

Your email address will not be published. Required fields are marked *