Session at JavaOne come in various shapes. Some are visionary, high level and future oriented. Others are detailed and discuss practical, sometimes very fine grained topics. One of the sessions I attended was somewhere in between: future oriented yet pretty concrete at the same time. It was probably my favorite session at JavaOne this year: What’s new in Servlet3.1: An Overview by Shing Wai Cha and Rajiv Mordani. The passion of the presenters – their enthusiasm to explain the current development of the Servlet API and their ability to demonstrate some rather tough concepts were pivotal. Many presenters can take their style as an example they can learn from.
The Servlet 3.1 API is part of the JEE 7 edition- that is scheduled for complete release in April 2013. The Servlet Specification is largely complete, although some elements are still under discussion as became clear during this talk. The probably most important new requirement in Servlet 3.1 is the ability to do Web Socket interaction. Web Sockets is a relatively new communication protocol that runs over TCP/IP and goes beyond HTTP in several respects. Web Sockets support bi-directional interactions (open channels through which both participating parties can send messages) with low overhead (no HTTP headers involved for example). Web Sockets is the main driver behind server push to client.
Note: even today, Java Web Applications can incorporate Web Sockets through various frameworks, both open source and commercial. See for example: https://technology.amis.nl/2012/01/15/stand-alone-java-client-for-jwebsocket-server-communicating-from-java-client-to-web-clients-and-vice-versa-over-websockets/.
Shing and Rajiv did a good job of explaining how Web Socket support is added to Servlets, building on top of the work in the 3.0 specification (JEE 6) on asynchronous processing (see for example https://blogs.oracle.com/enterprisetechtips/entry/asynchronous_support_in_servlet_3 by Rajiv Mordani). Basically this support meant that processing a request could be delegated to an Asynchronous job that would not hold on to the thread until the response could be completed. Through the Asynchronous support, Servlet Containers can handle a larger load of requests (that receive somewhat delayed responses from back end services such as databases or web services).
Servlet 3.1 adds to this support in several ways. The end result is that Servlet 3.1 can upgrade an HTTP request to the Web Socket protocol. This establishes a channel on which both the client and the server can publish messages. Listeners on both ends are activated when a message from the other end of the channel arrives. When there is no communication over the channel – no threads are active. Not only do we get server to client push, we also get it in a scalable, non-resource intensive way. And we hook into the client Web Socket support in browsers or in other clients. As Web Sockets are supported in many programming languages, they also provide a ‘channel’ between Java and .NET, PHP or Ruby.
The first element Shing discussed in the presentation is the notion of a ReadListener that can be registered on the ServletInputStream. In case the input does not come available in its entirety when processing is started, a ReadListener can be made available to the container to invoke at any time there is more data to read from the request. The code for this looks as follows:
The CountDownLatch (see http://www.javamex.com/tutorials/threads/CountDownLatch.shtml) is used to bring the thread into a form of hibernation: it waits, does nothing and holds (virtually) no resources. This state is entered if the initial reading of the input cannot complete because at some point the input.isReady() method returns false – meaning that there is currently no data to be read. As soon as more data is available on the input, the ReadListener will have to take over and continue processing.
A simple example of the ReadListener is shown here:
It contains the onDataAvailable method that is invoked whenever the container has more data on the input to be processed. When all data is fed into the listener, the container will tell the listener about that facts by invoking the onAllDataRead method. In this case, the thread that was hibernated in the Servlet itself is released through the call to countDown().
Something similar applies to write part of the Servlet: if for some reason the output that has to be written cannot be written all at once – we can make use of a WriteListener that is invoked by the container whenever the output stream is available for additional writing.
The first example can be slightly extended to really leverage the non-blocking API. The Servlet would be rewritten to use an AsyncContext rather than the CountDownLatch. It then looks like this:
Before any sort of reading is done or any sort of listener is registered, an AsynchContext is initiated. Then the ReadListener is registered and as much input is read as possible. When all can be read in one go, the complete() method is invoked on the AsynchContext and the read listener is never invoked. However, as soon as the reading of input is interrupted before the entire content is processed, the synchronous reading is abandoned and the read listener will take over. When that listener finds that all input has been processed, it will complete the AsynchContext:
When we move into Web Socket territory, we take the next step. This is where the fun really starts.
A client may indicate in the initial HTTP Request (through an HTTP header) that is would like to switch to WebSocket for more advanced communication. If and when the Servlet can and will do so, the connection is said to be upgraded. This includes sending an 101 response header to the client and doing the Web Socket handshake:
The servlet code for this would be something like this:
Note that the servlet will step out of the interaction after doing the upgrade. It leaves it to the ProtocolHandler to take the conversation with the client over. The Servlet only speaks HTTP and the ProtocolHandler in this case can do Web Sockets.
The very very bare minimum of a ProtocolHandler is shown here:
It is initialized with a WebConnection from which it can also retrieve the HttpSession context. During initialization, it will typically set up a ReadListener to receive and process any data that arrives on the just established Web Socket channel.
A very simple example of a protocolhandler that does nothing but copying any input it receives to the output (channel), basically echoing to the client what the client sends in, can be found in the GlassFish samples collection:
Any time data comes in, the onDataAvailable method is invoked by the container. Processing takes place – the data is read and combined in a StringBuilder objects whose content is subsequently written to the output. In a more serious application, some form of processing would take place on the input – for example retrieving data from a database or web service based on the parameters sent over the Web Socket channel – and the results would be written to the output, possibly in more than one go through the use of a WriteListener.
With the latest promoted GlassFish build, it is already possible to build and test examples of using the Web Socket support in Servlet 3.1 API.
Resources
Download the presentation from the JavaOne Content Catalog.
See the JCP specification request for Servlet 3.1 – JSR-340: http://jcp.org//en/jsr/detail?id=340
See the source code for the EchoProtocolHandler in the early builds for GlassFish 4.0: http://java.net/projects/glassfish/sources/svn/revision/56114