Java EE 7 SDK

Samples Main Page

# The Non-blocking I/O Read Servlet Sample Application

This sample application demonstrates how to use the non-blocking I/O functionality in servlets.

## Description

In this sample application, the client (`ClientTest`) sends two parts of data to the server (`ServerTest`). The server registers a `ReadListener`, whose methods are called when events related to the input stream occur. The implementation of `ReadListener` in this application collects data and echoes it back in reverse order.

Client

In `ClientTest`, the client initiates an HTTP connection to the server and writes two parts of data with a two second pause between them, which simulates blocking from the client side.

    @WebServlet(name = "ClientTest", urlPatterns = {"/"})
    public class ClientTest extends HttpServlet {
        OutputStream output = null;
        InputStream input = null;

        protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                                     throws ServletException, IOException {
            ...
            String urlPath = "http://"
                + request.getServerName()
                + ":" + request.getLocalPort() //default http port is 8080
                + request.getContextPath()
                + "/ServerTest";

            URL url = new URL(urlPath);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            ...
            conn.connect();
            ...
            output = conn.getOutputStream();
    		String firstPart = "Hello";
            writeData(output, firstPart);
            Thread.sleep(2000);
            ...
    		// Sending the second part
            input = conn.getInputStream();
            printEchoData(out, input);
        }

        protected void writeData(OutputStream output, String data) throws IOException {
            if (data != null && !data.equals("") && output != null) {
                output.write(data.getBytes());
                output.flush();
            }
        }

        protected void printEchoData(PrintWriter out, InputStream input) throws IOException {
            while (input.available() > 0 && input != null && out != null) {
                out.print((char) input.read());
            }
            out.println("</br>");
        }
    }

The servlet uses `OutputStream` and `InputStream` to write and read data, and `Thread.sleep()` to pause the thread for two seconds to simulate I/O blocking. You can send larger blocks of data instead of "Hello World".

Server

In `ServerTest`, the server receives the request, starts the asynchronous processing of the request, and registers a `ReadListener`

    @WebServlet(name="ServerTest", urlPatterns={"/ServerTest"}, asyncSupported = true)
    public class ServerTest extends HttpServlet {

        protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                                      throws ServletException, IOException {
            ...
            final AsyncContext context = request.startAsync();
            final ServletInputStream input = request.getInputStream();
            final ServletOutputStream output = response.getOutputStream();
            input.setReadListener(new ServerReadListenerImpl(input, output, context));
        }
        ...
    }

`@WebServlet(..., asyncSupported = true)` is an annotation that specifies the servlet name, its URL, and enables asynchronous processing. The `setReadListener()` method registers a read listener for the input stream.

Note: Non-blocking I/O only works with asynchronous request processing in servlets and filters.

Implementation of the Read Listener

`ReadListenerImpl.java` is the implementation of the `ReadListener` interface:

    public class ReadListenerImpl implements ReadListener {

        public ServerReadListenerImpl() {
            ...
        }

        @Override
        public void onDataAvailable() {
            ...
            while (input.isReady() && !input.isFinished()) {
                sb.append((char) input.read()); // Use StringBuilder to append chars together
            }
        }

        @Override
        public void onAllDataRead() {
            ...
            output.print("Echo the reverse String from server: " + sb.reverse().toString() + "<br>");
            output.flush();
            ...
        }

        @Override
        public void onError(Throwable t) {
            ...
            System.out.println("--> onError");
        }

The `onDataAvailable()` method is invoked when data is available to be read from the input request stream. The container subsequently invokes the `read()` method if and only if `isReady()` returns true. The `onAllDataRead()` method is invoked when all the data from the request has been read. The `onError(Throwable t)` method is invoked if there is any error or exceptions occurs while processing the request. The `isReady()` method returns true if the underlying data stream is not blocked. At this point, the container invokes the `onDataAvailable()` method.

You can customize the constructor to handle different parameters. Usually, the parameters are `ServletInputStream`, `ServletOutputStream`, or `AsyncContext`. This sample uses all of them to implement the `ReadListener` interface.

## Key Features

This sample application demonstrates the following key features:

- Implementation of the `ReadListener` interface to avoid waiting for data inside a loop.
- Asynchronous processing in servlets.

## Building, Deploying, and Running the Application

Following these instructions for building, deploying, and running this sample application:

1. Set up your build environment and configure the application server with which the build system has to work by following the common build instructions.
2. `app_dir` is the sample application base directory: `samples_install_dir/servlet/non-blocking-io-read-war`.
3. ````Change directory to `app_dir.
   `
4. Build, deploy, the sample application using the `mvn` target:

Use the command below to run this sample using the Cargo framework:

`app_dir> mvn clean verify cargo:run`

You can point Cargo to an already installed and running Glassfish server:

`app_dir> mvn clean verify cargo:run -Dglassfish.home=$<glassfish_dir>` (e.g. ../glassfish4)

You can also build, deploy the sample application without Cargo:

`app_dir> mvn install`

`app_dir> asadmin deploy ./target/<app_name>.war`

5. Run the sample as follows:
   - Use the browser to access
     `http://<javaee.server.name>:<javaee.server.port>/non-blocking-io-read-war`
     The expected message in the browser is the following:
     `Sending to server: Hello
     Sending to server: World
     Echo data from server: dlroW olleH
     Please check server log for details.`
6. Use the glassfish command line to undeploy the application.

   `app_dir> asadmin undeploy` <app_name>

7. Use the target `clean` to remove the temporary directories like /target.

   `app_dir> mvn clean`

## Building, Deploying, and Running the Application in NetBeans IDE

Perform the following steps to build, deploy, and run the application using NetBeans IDE:

1. Refer to the common build instructions for setting up NetBeans IDE and Java EE 7 SDK.
2. In the NetBeans IDE, choose File → Open Project (Ctrl-Shift-O), navigate to the `samples_install_dir/servlet/` directory, select `non-blocking-io-read-war`, and click Open Project.
3. In the Projects tab, right click `non-blocking-io-read-war` and select Run to build, deploy, and run the project.

## Troubleshooting

If you have problems when running the application, refer the troubleshooting document.

Copyright © 1997-2013 Oracle and/or its affiliates. All rights reserved.