Reverse Ajax, Part 3: Web servers and Socket.IO

Using various web containers, APIs, and abstraction libraries

This series explores how to develop event-driven web applications using Reverse Ajax techniques. Part 1 introduced different ways to implement a Reverse Ajax communication: polling, piggyback, and Comet, using long polling and streaming. Part 2 explained how to implement Reverse Ajax using WebSockets and discussed the limitations of web servers using Comet and WebSockets. In this article, learn how to use Comet and WebSockets in your web application for various web containers and APIs. Also learn about Socket.IO, an abstraction library that can be used in a web application with Reverse Ajax. Abstraction libraries, which can be used transparently, hide all of the complexity behind Comet and WebSockets.

Share:

Mathieu Carbou, Java Web Architect, Ovea

Mathieu Carbou photoMathieu Carbou, a Java web architect and consultant at Ovea, provides services and development solutions. He is a committer and leader of several open source projects, a speaker, and a leader of Montreal's Java User Group. Mathieu has a strong background in code design and best practices, and is a specialist of event-driven web development from the client side to the back end. He focuses on providing event-driven and messaging solutions in highly scalable web applications. Check out his blog.



30 August 2011

Also available in Chinese Russian Japanese Vietnamese

Introduction

Today's users expect fast, dynamic applications accessible from the web. This series shows you how to develop event-driven web applications using Reverse Ajax techniques. Part 1 introduced Reverse Ajax, polling, streaming, Comet, and long polling. You learned how Comet using HTTP long polling is the best way to reliably implement Reverse Ajax, since all browsers now provide support. Part 2 showed how to implement Reverse Ajax using WebSockets. Code examples helped illustrate WebSockets, FlashSockets, constraints on the server side, request-scoped services, and pausing long-lived requests.

In this article, delve into details about using Comet and WebSockets in your web application for different web containers and APIs (Servlet 3.0 and Jetty Continuations). Learn to transparently use Comet and WebSockets by using abstraction libraries, such as Socket.IO. Socket.IO uses feature detection to decide if the connection will be established with WebSocket, AJAX long polling, Flash, and so on.

You can download the source code used in this article.

Prerequisites

Ideally, to get the most out of this article, you should know JavaScript and Java. The example created in this article was built using Google Guice, a dependency injection framework written in Java. To follow along with this article, you should be familiar with the concepts of a dependency injection framework, such as Guice, Spring, or Pico.

To run the sample in this article, you will also need the latest version of Maven and the JDK (see Resources).


Server solutions for Comet and WebSocket

You learned in Part 1 that Comet (long-polling or streaming) requires that the server is able to pause a request and resume or complete it after a potentially long delay. Part 2 described how servers need to use the non-blocking I/O features to handle a lot of connections and that they only use threads to serve requests (thread-per-request model). You also learned that the usage of WebSocket is server-dependent and that not all servers support WebSockets.

This section shows you how to use Comet and WebSockets, if applicable, on the Jetty, Tomcat, and Grizzly web servers. The source code provided with this article contains a sample chat web application for Jetty and Tomcat. This section also discusses the supported APIs for the following application servers: Jboss, Glassfish, and WebSphere.

Jetty

Jetty is a web server supporting the Java Servlet specification 3.0, WebSockets, and many other integration specifications. Jetty is:

  • Powerful and flexible
  • Easily embedded
  • Supports virtual hosts, session clustering, and a lot of features that can be easily configured through Java code or XML
  • Used for the Google App Engine's hosting service

The core Jetty project is hosted by the Eclipse Foundation.

Since Version 6, Jetty includes an asynchronous API called Jetty Continuations, which allows a request to be paused and resumed later. Table 1 shows the map of the supporting specification and API for the major Jetty version families.

Table 1. Jetty versions and support
SupportsJetty 6Jetty 7Jetty 8
Non-blocking I/OXXX
Servlet 2.5XXX
Servlet 3.0XX
Jetty Continuations (Comet)XXX
WebSocketsXX

To implement Reverse Ajax with Comet, you can use Jetty's Continuation API, as shown in Listing 1:

Listing 1. Jetty Continuation API for Comet
// Pausing a request from a servlet's method (get, post, ...):

protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException { 

Continuation continuation = ContinuationSupport.getContinuation(req); 
// optionally set a timeout to avoid suspending requests for too long 
continuation.setTimeout(0); 
// suspend the request 
continuation.suspend(); 
// then hold a reference for future usage from another thread 
continuations.offer(continuation); 

}

// Then, from another thread which wants to send an event to the client:

while (!continuations.isEmpty()) { 
    Continuation continuation = continuations.poll(); 
    HttpServletResponse response = 
        (HttpServletResponse) continuation.getServletResponse(); 
    // write to the response 
    continuation.complete(); 
}

The complete web application is in the source code that comes with this article. The Jetty Continuations are bundled in a JAR archive. You have to put this JAR file in your web application's WEB-INF/lib folder to be able to use Jetty's Comet features. The Jetty Continuations will work on Jetty 6, 7, and 8.

Beginning with Jetty 7, you also have access to the WebSockets feature. Place Jetty's WebSocket JAR file in your web application's WEB-INF/lib folder to gain access to Jetty's WebSocket API, as shown in Listing 2:

Listing 2. Jetty's WebSocket API
// Implement the  doWebSocketConnect and returns an implementation of
//  WebSocket:

public final class ReverseAjaxServlet extends WebSocketServlet { 
    @Override 
    protected WebSocket doWebSocketConnect(HttpServletRequest request,
                                           String protocol) { 
        return [...] 
    } 
}

// Sample implementation of  WebSocket:

class Endpoint implements WebSocket { 
    Outbound outbound; 
    public void onConnect(Outbound outbound) { 
        this.outbound = outbound;    
    } 
    public void onMessage(byte opcode, String data) { 
        outbound.sendMessage("Echo: " + data);
        if("close".equals(data)) 
             outbound.disconnect();
    } 
    public void onFragment(boolean more, byte opcode, 
                           byte[] data, int offset, int length) { 
    } 
    public void onMessage(byte opcode, byte[] data, 
                          int offset, int length) { 
        onMessage(opcode, new String(data, offset, length)); 
    } 
    public void onDisconnect() { 
        outbound = null; 
    } 
}

In the downloadable source code, there is a chat sample in the jetty-websocket folder that demonstrates how to use Jetty's WebSocket API.

Tomcat

Tomcat is probably the most widely known web server. It has been used for many years and was integrated as a web container into early versions of the Jboss application server. Tomcat was also used as the reference implementation for the servlet specification. It was discontinued in servlet API 2.5 when people began to look at alternatives based on non-blocking I/O (such as Jetty). Table 2 shows the supported specifications and API for the two latest Tomcat version families.

Table 2. Tomcat support
SupportsTomcat 6Tomcat 7
Non-blocking I/OXX
Servlet 2.5XX
Servlet 3.0X
Advanced I/O (Comet)XX
WebSockets

As shown in Table 2, Tomcat does not support WebSockets; it has an equivalent of Jetty's Continuations called Advanced I/O to support Comet. Advanced I/O is much more of a low-level wrapper around NIO than a good API to facilitate Comet usage. It is poorly documented, and there are few examples of applications using this API. Listing 3 shows a servlet example used to suspend and resume requests in a chat web application. You can find the complete web application in the source code with this article.

Listing 3. Tomcat API for Comet
public final class ChatServlet extends HttpServlet 
                               implements CometProcessor { 

    private final BlockingQueue<CometEvent> events = 
         new LinkedBlockingQueue<CometEvent>(); 

    public void event(CometEvent evt) 
        throws IOException, ServletException { 

        HttpServletRequest request = evt.getHttpServletRequest(); 
        String user = 
            (String) request.getSession().getAttribute("user"); 
        switch (evt.getEventType()) { 
            case BEGIN: { 
                if ("GET".equals(request.getMethod())) { 
                    evt.setTimeout(Integer.MAX_VALUE); 
                    events.offer(evt); 
                } else { 
                    String message = request.getParameter("message"); 
                    if ("/disconnect".equals(message)) { 
                        broadcast(user + " disconnected"); 
                        request.getSession().removeAttribute("user"); 
                        events.remove(evt); 
                    } else if (message != null) { 
                        broadcast("[" + user + "]" + message); 
                    } 
                    evt.close(); 
                } 
            } 
        } 
    } 

    void broadcast(String message) throws IOException { 
        Queue<CometEvent> q = new LinkedList<CometEvent>(); 
        events.drainTo(q); 
        while (!q.isEmpty()) { 
            CometEvent event = q.poll(); 
            HttpServletResponse resp = event.getHttpServletResponse();
            resp.setStatus(HttpServletResponse.SC_OK);
            resp.setContentType("text/html");
            resp.getWriter().write(message);
            event.close(); 
        } 
    } 
}

In Tomcat, an asynchronous servlet must implement a CometProcessor. For asynchronous servlets, Tomcat does not call the standard HTTP methods (doGet, doPost, and so on). Instead, an event is sent to the event(CometdEvent) method. When a request first arrives, the example checks to see if it is a GET in order to suspend it; evt.close() is not called. If it is a POST, it means the user sent a message, so it is broadcast to the other CometEvent, and evt.close() is called to complete the post request. On the client side, broadcasting will make all long polling requests to complete the sent message, and another long polling request is immediately sent to receive the next events.

Grizzly and Glassfish

Grizzly is not a web container, but more of an NIO framework that helps developers build scalable applications. It was developed as part of the Glassfish project, but it can also be used on a standalone or embedded basis. Grizzly provides components to act as an HTTP/HTTPS server and components for Bayeux Protocol, Servlet, HttpService OSGi, and Comet, among others. Grizzly supports WebSockets and is used in Glassfish to provide Comet and WebSocket support.

Glassfish, Oracle's Application Server, is the reference implementation of the J2EE 6 specifications. Glassfish is a complete suite, like WebSphere and Jboss, using Grizzly for NIO, WebSocket, and Comet support. Its modular architecture, based on OSGI, makes it really flexible for changing components. Table 3 shows Glassfish support for Comet and WebSockets.

Table 3. Glassfish support
SupportsGlassfish 2Glassfish 3
Non-blocking I/OXX
Servlet 2.5XX
Servlet 3.0X
CometXX
WebSocketsX

Grizzly usage is not trivial, as it's intended to be used embedded or directly from Java code. It is widely used as a framework to support Comet and WebSockets that can be embedded in a larger application, such as Glassfish, which provides web deployment capabilities and the Servlet specification API.

See Resources for links to examples of WebSockets and Comet in Grizzly or Glassfish. Because Glassfish uses Grizzly, the examples should work on both. The WebSocket API is very similar to the one in Jetty, but the Comet API is more complex.

Jboss

Jboss is an application server built on top of Tomcat. It has supported Comet and NIO since Version 5. Jboss 7 is still in development, but is included in Table 4 below.

Table 4. Jboss support
SupportsJboss 5Jboss 6Jboss 7
Non-blocking I/OXXX
Servlet 2.5XXX
Servlet 3.0XX
CometXXX
WebSockets

WebSphere

WebSphere is an IBM application server. Support of the Servlet 3 API (containing the standardized asynchronous API for Comet) was added in Version 8 of WebSphere (see Resources to read the announcement).

Table 5. WebSphere support
SupportsWebSphere 8
Non-blocking I/OX
Servlet 2.5X
Servlet 3.0X
CometX
WebSockets

What about having common APIs?

Each server brings its own native API for Comet and WebSocket. As you might guess, writing a portable web application can be difficult. The Servlet 3.0 Specification includes additional methods to suspend and resume a request later, allowing all web containers supporting the Servlet 3.0 Specification to support the Comet long-polling requests.

The Jetty team provides a library called Jetty Continuation, which is independent from the Jetty container. The Jetty Continuation library is clever enough to detect the container or specification available. If running on a Jetty server, the native Jetty API will be used. If running on a container supporting the Servlet 3.0 Specification, this common API will be used. Otherwise, a non-scalable implementation is used.

Regarding WebSockets, there is no standard yet in Java, and thus you need to use the container vendor API within your web application if you want to use WebSockets.

Table 6 summarizes the technologies supported by the various servers.

Table 6. Technologies supported by servers
ContainerCometWebSocket
Jetty 6Jetty ContinuationsN/A
Jetty 7Servlet 3.0
Jetty Continuations
Native Jetty API
Jetty 8Servlet 3.0
Jetty Continuations
Native Jetty API
Tomcat 6Advanced I/ON/A
Tomcat 7Servlet 3.0
Advanced I/O
Jetty Continuations
N/A
Glassfish 2Native Grizzly APIN/A
Glassfish 3Servlet 3.0
Native Grizzly API
Jetty Continuations
Native Grizzly API
Jboss 5Native Jboss APIN/A
Jboss 6Servlet 3.0
Native Jboss API
Jetty Continuations
N/A
Jboss 7Servlet 3.0
Native Jboss API
Jetty Continuations
N/A
WebSphere 8Servlet 3.0
Jetty Continuations
N/A

There is no obvious solution for WebSockets except to use the container API. As for Comet, each container supporting the Servlet 3.0 Specification supports Comet. The advantage of Jetty Continuations here is to provide Comet support on all of these containers. Thus, some Reverse Ajax libraries (which is discussed in the next section and in the next article in this series) are using Jetty Continuations for their server-side API.

The Jetty Continuation API is shown in the Jetty sample in this article. The Servlet 3.0 Specification was described and used in the two Comet examples in Part 1 of this series.


Abstraction libraries

Considering all of the major APIs (Servlet 3.0 and Jetty Continuations), plus all of the native support on the server side, and two major ways of doing Reverse Ajax on the client side (Comet and WebSocket), writing your own JavaScript and Java code to wire them could be difficult. You must also factor in timeouts, connection failures, acknowledgement, ordering, buffering, and so on.

The rest of this article shows you Socket.IO in action. Part 4 in this series will explore Atmosphere and CometD. These three libraries are all open source, and they all support Comet and WebSocket on many servers.

Socket.IO

Socket.IO is a JavaScript client library that provides a single API, similar to WebSocket, to connect to a remote server to asynchronously send and receive messages. By providing a common API, Socket.IO supports several transports: WebSocket, Flash Sockets, long polling, streaming, forever Iframes, and JSONP polling. Socket.IO detects browser capabilities and tries to choose the best transport available. The Socket.IO library is compatible with nearly all browsers (including old ones, such as IE 5.5) as well as mobile browsers. It also has features such as heartbeats, timeout, disconnection, and error handling.

The Socket.IO website (see Resources) describes in detail how the library works and which browser and Reverse Ajax technique are used. Basically, Socket.IO uses a communication protocol that enables the client library to communicate with an endpoint on the server side, which can understand the Socket.IO protocol. Socket.IO was first developed for Node JS, a JavaScript engine used to build faster servers. Many projects brought support for other languages, including Java.

Listing 4 shows an example of using the Socket.IO JavaScript library on the client side. The Socket.IO website has documentation and examples.

Listing 4. Socket.IO client library usage
var socket = new io.Socket(document.domain, { 
    resource: 'chat' 
}); 
socket.on('connect', function() { 
    // Socket.IO is connected
}); 
socket.on('disconnect', function(disconnectReason, errorMessage) { 
    // Socket.IO disconnected
}); 
socket.on('message', function(mtype, data, error) { 
    // The server sent an event
});

// Now that the handlers are defined, establish the connection:
socket.connect();

To use the Socket.IO JavaScript library, you will need the corresponding Java part called Socket.IO Java (see Resources). This project was initially started by the Apache Wave team to bring support for Reverse Ajax to Wave before WebSockets was available. Socket.IO Java was forked and maintained by Ovea (a company specializing in event-driven web development) and then abandoned. Developing the back end to support the Socket.IO client library is complex due to the multiple transports. Part 4 of this series will show how supporting a lot of transports in a client library is not necessary to achieve better scalability and browser support, since long-polling and WebSockets are sufficient. When WebSockets were not available, Socket.IO was indeed a good choice.

Socket.IO Java uses the Jetty Continuation API to suspend and resume requests. It uses the native Jetty WebSockets API for WebSockets support. You can determine which server will work correctly with a web application using Socket.IO Java.

Listing 5, below, shows an example of how to use Socket.IO on the server. You have to define a servlet extending SocketIOServlet and implement a method returning a sort of endpoint representation. The API is very similar to the WebSockets API. The advantage is that this API is used on the server side, independent of the chosen transport on the client side. Socket.IO translates all transport types to the same API on the server side.

Listing 5. Socket.IO Java library used for the chat sample - servlet
public final class ChatServlet extends SocketIOServlet { 
    private final BlockingQueue<Endpoint> endpoints = 
        new LinkedBlockingQueue<Endpoint>(); 
 
    @Override 
    protected SocketIOInbound doSocketIOConnect
                                        (HttpServletRequest request) { 
        String user = 
                (String) request.getSession().getAttribute("user"); 
        return user == null ? null : new Endpoint(this, user, request); 
    } 
 
    void broadcast(String data) { 
        for (Endpoint endpoint : endpoints) { 
            endpoint.send(data); 
        } 
    } 
 
    void add(Endpoint endpoint) { 
        endpoints.offer(endpoint); 
    } 
 
    void remove(Endpoint endpoint) { 
        endpoints.remove(endpoint); 
    } 
}

Listing 6 shows how to return the endpoint.

Listing 6. Socket.IO Java library usage for the chat sample - endpoint
class Endpoint implements SocketIOInbound { 
 
    [...]
 
    private SocketIOOutbound outbound; 
 
    [...]

    @Override 
    public void onConnect(SocketIOOutbound outbound) { 
        this.outbound = outbound; 
        servlet.add(this); 
        servlet.broadcast(user + " connected"); 
    } 
 
    @Override 
    public void onDisconnect(DisconnectReason reason, 
                             String errorMessage) { 
        outbound = null; 
        request.getSession().removeAttribute("user"); 
        servlet.remove(this); 
        servlet.broadcast(user + " disconnected"); 
    } 
 
    @Override 
    public void onMessage(int messageType, String message) { 
        if ("/disconnect".equals(message)) { 
            outbound.close(); 
        } else { 
            servlet.broadcast("[" + user + "] " + message); 
        } 
    } 
 
    void send(String data) { 
        try { 
            if (outbound != null 
    && outbound.getConnectionState() == ConnectionState.CONNECTED) { 
                outbound.sendMessage(data); 
            } 
        } catch (IOException e) { 
            outbound.close(); 
        } 
    } 
 
}

The complete example of Socket.IO is included in the source code in the socketio folder.


Conclusion

All web containers support Comet, and nearly all support WebSockets. Even if the specifications lead to several different native implementations, you can still develop a web application using Comet with the common APIs (Servlet 3.0 or Jetty Continuations). And, even better, you can harness the power of Comet and WebSockets transparently by using libraries such as Socket.IO. Two more libraries, Atmosphere and CometD, will be covered in the next article in this series. All three libraries provide multi-browser support, a fantastic user experience, and the benefits of error handling, an easier API, timeout, and reconnections.


Download

DescriptionNameSize
Article source codereverse_ajaxpt3_source.zip21KB

Resources

Learn

Get products and technologies

  • Jetty: Get Jetty, a web server and javax.servlet container, plus support for WebSockets.
  • Apache Tomcat Advanced I/O documentation: Get valuable links, a User Guide, Reference, and Apache Tomcat Development instructions.
  • Grizzly NIO Framework: To help you take advantage of the Java NIO API.
  • Grizzly Comet Sample: See the changes you'll need to make to existing applications before building a new one from scratch.
  • Glassfish Application Server: Get the main GlassFish Server Open Source Edition.
  • Socket.IO Java: Get the original project and the most updated Ovea's fork.
  • Apache Maven: Get Maven, a software project management and comprehension tool.
  • Java Development Kit, Version 6: Get the Java Platform, Standard Edition (Java SE), which lets you develop and deploy Java applications on desktops and servers, as well as in today's demanding embedded environments.
  • Try out IBM software for free. Download a trial version, log into an online trial, work with a product in a sandbox environment, or access it through the cloud. Choose from over 100 IBM product trials.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=754325
ArticleTitle=Reverse Ajax, Part 3: Web servers and Socket.IO
publish-date=08302011