As I have mentioned in my previous blog, HCP has introduced a new runtime that is JAVA Web Tomcat 8 which is the natural predecessor of JAVA Web Tomcat 7 runtime. Also in future a Certified EE 7 runtime is planned. The following diagram will show the release flow of the runtimes:

Capture.PNG

Tomcat 8 introduced Servlet 3.1. The main topic that we will concentrate on in this blog is to see how non-blocking IO feature works in Servlet 3.1.

In a typical Servlet scenario we see generally three kinds of operations. Read, Write and all other operations. The following diagram will show the typical flow of these operations:

Capture1.PNG

However, since the Read and Write operations are synchronous the Task 2 will wait until task 1 is complete. And here is the issue for very high volume of data streaming. Task 2 will wait long enough to degrade the performance. In Servlet 3.1 the IO mechanism has been put in a asynchronous manner. The following diagram explains the flow:

Capture2.PNG

Servlet 3.1 when runs on a asynchronous mode, registers two listener classes – For reading data you need to implement a class with interface ReadListener. For Writing data out you need to implement a class with interface WriteListener. You can find the Javadoc for these interfaces in the links below:

ReadListener (Java(TM) EE 7 Specification APIs)

WriteListener (Java(TM) EE 7 Specification APIs)

So, let’s try to create a Servlet and try out non-blocking IO feature.

Pre-requisite: As I mentioned in my previous blog please set up your local environment for Java Web Tomcat 8 runtime.

Step 1: Create a dynamic web project with the following option: Target Runtime = Tomcat 8; Dynamic web module = 3.1

Capture.PNG

Next -> Next -> Check on Generate web.xml and Finish.

This will create the project in your eclipse:

Capture.PNG

Step 2: Create Servlet. I created a servlet ‘UploadServlet’ under package com.sap.web.

Important point that we need to mention asyncSupported=true in the @WebServlet annotation.

@WebServlet(name = “UploadServlet”, urlPatterns = {“/UploadServlet”}, asyncSupported=true)

In this case I just implemented the doPost method, however similar approach can be taken for doGet.

Next get the context of the asynch  request

AsyncContext context = request.startAsync();

And then you need to setup the listener on that context.

      // set up async listener

        context.addListener(new AsyncListener() {

            public void onComplete(AsyncEvent event) throws IOException {

                event.getSuppliedResponse().getOutputStream().print(“Complete”);

            }

            public void onError(AsyncEvent event) {

                System.out.println(event.getThrowable());

            }

            public void onStartAsync(AsyncEvent event) {

            }

            public void onTimeout(AsyncEvent event) {

                System.out.println(“my asyncListener.onTimeout”);

            }

        });

Now get the InputStearm object and instantiate your read Listener class.

      ServletInputStream input = request.getInputStream();

        ReadListener readListener = new ReadListenerImpl(input, response, context);

        input.setReadListener(readListener);

Step 3: Now we need to create the ReadListnerImpl class which implements the ReadListener interface.

This Interface gives you some call back functions:

onDataAvailable() : This is the call back function that fires when the data is available in IO. So this is the right place to read the Stream.

        StringBuilder sb = new StringBuilder();

        int len = -1;

        byte b[] = new byte[1024];

        while (input.isReady() && (len = input.read(b)) != -1) {

            String data = new String(b, 0, len);

            sb.append(data);

        }

and put this in a queue.

queue.add(sb.toString());

Note: queue is defined as a LinkedBlockingQueue

private Queue queue = new LinkedBlockingQueue();

Similarly you can define actions to do when all IO data are read in the following method: onAllDataRead()

In this simplistic example we kick started writing IO in this method.

    public void onAllDataRead() throws IOException {

        System.out.println(“Data is all read”);

        // now all data are read, set up a WriteListener to write

        ServletOutputStream output = res.getOutputStream();

        WriteListener writeListener = new WriteListenerImpl(output, queue, ac);

        output.setWriteListener(writeListener);

    }

In error handling should be done in method: public void onError(final Throwable t)

    public void onError(final Throwable t) {

        ac.complete();

        t.printStackTrace();

    }

Step 4: Implement WriteListener.

Now we need to create a class implementing interface WriteListener.

public class WriteListenerImpl implements WriteListener

The write operation is defined in the call back method onWritePossible()

    public void onWritePossible() throws IOException {

        while (queue.peek() != null && output.isReady()) {

            String data = (String) queue.poll();

            output.print(data);

        }

        if (queue.peek() == null) {

            context.complete();

        }

    }

And the error handling has to be done in onError method:

    public void onError(final Throwable t) {

        context.complete();

        t.printStackTrace();

    }

Testing

Now in order to test it I added a small HTML page that posts data via HTTP POST. (You could have used POSTMAN or Advanced REST console as well)

The code snippet for the HTML code is like below:

<!DOCTYPE html>

<html>

<head>

<meta charset=”ISO-8859-1″>

<title>Document Service Test</title>

</head>

<body>

  <fieldset>

  <legend>Upload File</legend>

  <form action=’UploadServlet’ method=’post’ enctype=’multipart/form-data’>

  <label for=’filename’>File: </label>

  <input id=’filename’ type=’file’ name=’filename’ size=’50’/>

  <br/>

  <input type=’submit’ value=’Upload File’/>

  </form>

  </fieldset>

</body>

</html>

Deployment:

Now that the coding is complete I would like to run that. For that I need to deploy the project in my local runtime.

From the Server view right click and create a new server.

Choose Tomcat 8 under SAP as the server type.

Capture.PNG

Add the newly created project and finish.

Once published, you can run a test by running the url: http://localhost:<port>/DocumentTest.html

Capture.PNG

Note: In this example we are converting the stream in String and writing it back. So we will test with a text file upload.

Original File content:

<!DOCTYPE html>

<html>

<head>

<meta charset=”ISO-8859-1″>

<title>Insert title here</title>

</head>

<body>

<h2>Testing non blocking IO</h2>

</body>

</html>

Result:

Capture.PNG

For your reference I am attaching the source code of the main Java files.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply