Tuesday Nov 27, 2012

Non-blocking I/O using Servlet 3.1: Scalable applications using Java EE 7 (TOTD #188)


Servlet 3.0 allowed asynchronous request processing but only traditional I/O was permitted. This can restrict scalability of your applications. In a typical application, ServletInputStream is read in a while loop.
public class TestServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
         throws IOException, ServletException {    
ServletInputStream input = request.getInputStream();
       byte[] b = new byte[1024];
       int len = -1;
       while ((len = input.read(b)) != -1) {
          . . .
       }
   }
}
If the incoming data is blocking or streamed slower than the server can read then the server thread is waiting for that data. The same can happen if the data is written to ServletOutputStream.

This is resolved in Servet 3.1 (JSR 340, to be released as part Java EE 7) by adding event listeners - ReadListener and WriteListener interfaces. These are then registered using ServletInputStream.setReadListener and ServletOutputStream.setWriteListener. The listeners have callback methods that are invoked when the content is available to be read or can be written without blocking.

The updated doGet in our case will look like:

AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));

Invoking setXXXListener methods indicate that non-blocking I/O is used instead of the traditional I/O. At most one ReadListener can be registered on ServletIntputStream and similarly at most one WriteListener can be registered on ServletOutputStream. ServletInputStream.isReady and ServletInputStream.isFinished are new methods to check the status of non-blocking I/O read. ServletOutputStream.canWrite is a new method to check if data can be written without blocking.

 MyReadListener implementation looks like:

@Override
public void onDataAvailable() {
try {
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);
System.out.println("--> " + data);
}
} catch (IOException ex) {
Logger.getLogger(MyReadListener.class.getName()).log(Level.SEVERE, null, ex);
}
}

@Override
public void onAllDataRead() {
System.out.println("onAllDataRead");
context.complete();
}

@Override
public void onError(Throwable t) {
t.printStackTrace();
context.complete();
}

This implementation has three callbacks:
  • onDataAvailable callback method is called whenever data can be read without blocking
  • onAllDataRead callback method is invoked data for the current request is completely read.
  • onError callback is invoked if there is an error processing the request.
Notice, context.complete() is called in onAllDataRead and onError to signal the completion of data read.

For now, the first chunk of available data need to be read in the doGet or service method of the Servlet. Rest of the data can be read in a non-blocking way using ReadListener after that. This is going to get cleaned up where all data read can happen in ReadListener only.

The sample explained above can be downloaded from here and works with GlassFish 4.0 build 64 and onwards.

The slides and a complete re-run of What's new in Servlet 3.1: An Overview session at JavaOne is available here.

Here are some more references for you:

Monday May 17, 2010

TOTD #136: Default Error Page using Servlets 3.0 - Improved productivity using Java EE 6

Servlets 2.x allowed to create a mapping between an HTTP error code or an exception type to the path of a resource in the Web application. This is achieved by specifying an "error-page" element in the "web.xml". The element definition looks like:

So any HTTP error code or an exception thrown within the application can be mapped to a resource bundled with the application. Here is a sample:

<error-page>
     <error-code>404</error-code>
     <location>/error-404.jsp</location>
</error-page>

Adding the above fragment in "web.xml" of an application will display "error-404.jsp" page to the client if a non-existing resource is accessed. This mapping can be easily done for other HTTP status codes as well by adding other <error-page> elements.

Similarly, <exception-type> element can be used to map an exception to a resource in the web application. This allows fine-grained mapping of errors from your web application to custom pages.

Starting with Servlets 3.0, <error-code> and <exception-type> elements are optional. An <error-page> without any <exception-type> and <error-code> will be considered as the webapp's default error page, and will act as a "catch-all" for any error codes or exception types. It will be an error if a web.xml contains more than one such default error page.

A default error page may be overridden for specific exception types and error codes. For example:

     <error-page>
       <location>/error-default.jsp</location>
     </error-page>

     <error-page>
       <error-code>404</error-code>
       <location>/error-404.jsp</location>
     </error-page>

Any response with a status code other than 404 will be error-dispatched to /default.jsp, while a 404 response will be error-dispatched to /error-404.jsp.

So if the Servlet code looks like:

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

    protected void doGet(HttpServletRequest request, 
                         HttpServletResponse response) throws IOException {
        String type = (String)request.getParameter("type");

        if (type == null) {
            response.getWriter().print("hello world"); 
            return;
        }

        if (type.equals("helloex")) {   
          throw new HelloException();
        } else if (type.equals("ncdfe")) {
            throw new NoClassDefFoundError();
        } else {
            throw new NullPointerException();
        }
    }
}

And the "web.xml" looks like:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <error-page>
       <exception-type>java.lang.NoClassDefFoundError</exception-type>
       <location>/error-ncdfe.jsp</location>
    </error-page>
    <error-page>
       <exception-type>server.HelloException</exception-type>
       <location>/error-helloex.jsp</location>
    </error-page>
    <error-page>
       <error-code>404</error-code>
       <location>/error-404.jsp</location>
    </error-page>
    <error-page>
       <location>/error-default.jsp</location>
    </error-page>
</web-app>

Lets say the directory structure looks like:

WEB-INF/classes/
WEB-INF/classes/server/
WEB-INF/classes/server/HelloException.class
WEB-INF/classes/server/HelloServlet.class
WEB-INF/web.xml
error-404.jsp
error-default.jsp
error-helloex.jsp
error-ncdfe.jsp

and this application is deployed as "DefaultErrorPage.war". Then here is a table of the page that gets displayed when the URL mentioned in the first column is accessed:

URL Response Comment
http://localhost:8080/DefaultErrorPage/HelloServlet "hello world" Expected result
http://localhost:8080/DefaultErrorPage/HelloServlet2 error-404.jsp HTTP 404
http://localhost:8080/DefaultErrorPage/HelloServlet?type=ncdfe error-ncdfe.jsp System exception
http://localhost:8080/DefaultErrorPage/HelloServlet?type=helloex error-helloex.jsp User exception
http://localhost:8080/DefaultErrorPage/HelloServlet?type error-default.jsp Catch-all exception


Try this and other Java EE 6 features in GlassFish Server Open Source Edition 3 or Oracle GlassFish Server today!

The complete source code used in this blog can be downloaded here.

The default setting in Chrome is to show suggestions to navigate to other parts of the website or search with Google. This can be easily disabled by Chrome -> Preferences -> Under the Hood and deselecting "Show suggestions for navigation errors" in Privacy section. This is explained in detail here.

Technorati: totd javaee glassfish v3 servlet default error

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today