Java EE 7 SDK

Samples Main Page

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

This is a simple servlet application that uses the non-blocking I/O API to demonstrate how to write to a response in a non-blocking manner.

## Description

This sample application demonstrates how to use the `WriteListener` interface in servlets. The application consists of three elements: the client (`ClientTest`), the server (`ServerTest`), and the implementation of the `WriteListener` interface (`WriteListenerImpl`). The server registers the write listener, and when data is available to write to the response, it sends chunks of bytes to the client. The chunk size is pre-configured to simulate blocking (the maximum write buffer size). The client receives the data from the server and displays it on the page.

Client

In `ClientTest.java`, the client initiates an HTTP connection to server and waits for the response to show it on the page.

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

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

            URL url = new URL(urlPath);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            ...
            conn.connect();
            ...
            BufferedReader input = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            for (String line; input != null && (line = input.readLine()) != null;) {
                if (line.length() > 20) {
                    out.println(line.substring(0, 20) + "</br>");
                    out.flush();
                } else if (line.equals("---> Time out")) {
                    out.println(line + "</br>");
                    out.flush();
                }
            }
        }
        ...
    }

Since the server sends several chunks of bytes, the length of the byte array is larger than the width of most browser windows, so some browsers would not display this correctly. In this sample, the length of the line is truncated to 20 characters.

Server

In `ServerTest.java`, after receiving a request the servlet starts the asynchronous request processing and registers the write listener.

    @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 ServletOutputStream output = response.getOutputStream();
            output.setWriteListener(new ServerWriteListenerImpl(output, context));
        }
    ...
    }

`WebServlet(..., asyncSupported = true)` is an annotation that specifies the servlet name, URL, and asynchronous processing support. The method `setWriteListener()` registers the write listener for the input stream.

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

Write Listener Implementation

`WriteListenerImpl.java` is the implementation of the `WriteListener` interface.

    public class WriteListenerImpl implements WriteListener {
        public WriteListenerImpl() {
            ...
        }

        @Override
        public void onWritePossible() throws IOException {
    	    ...
            while (output.isReady()) {
                if (System.currentTimeMillis() - startTime > 10000 || count > 10) {
                    output.println("---> Time out");
                    throw new Exception("Time out");
                }
                System.out.println("Writing data...... ");
                writeBytes(output, 'a');
                count++;
            }
            ...
        }

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

    	protected void writeBytes(ServletOutputStream output, char data) throws IOException {
            byte[] b = new byte[LENGTH];
            Arrays.fill(b, 0, LENGTH, (byte) data);
            output.write(b);
            ...
        }
    }

The method `onWritePossible()` is invoked when data is available to write to the response stream. The container subsequently invokes the `writeBytes()` method if and only if `isReady()` returns true. The `onError(Throwable t)` method is invoked if any error or exceptions occur while writing to the response. The `isReady()` method returns true if the underlying data stream is not blocked. At this point, the container invokes the `writeBytes()` method.

You can customize the constructor to handle different parameters. The parameters can be `ServletInputStream`, `ServletOutputStream`, or `AsyncContext`. This sample uses `ServletOutputStream` and `AsyncContext` to implement the write listener.

## Key Features

This sample application demonstrates the following key features:

- Implementing the `WriteListener` interface to avoid waiting for the output stream to be ready in the application.
- Asynchronous processing in servlets.

## Building, Deploying, and Running the Application

Follow 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-write-war`.
3. ````Change directory to `app_dir.
   `
4. Build and 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-write-war`
     The expected message in the browser is the following:

         This is the data from Server.
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         aaaaaaaaaaaaaaaaaaaa
         ---> Time out
         Check server log for more 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-write-war`, and click Open Project.
3. In the Projects tab, right click `non-blocking-io-write-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.