Reverse Ajax, Part 2: WebSockets

A powerful solution (despite some server constraints)

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. In this article, learn a new way to implement Reverse Ajax, using WebSockets, a new HTML5 API. WebSockets can be implemented natively by browser vendors or by using a bridge that delegates calls to a hidden Flash component called FlashSockets. This article also discusses some constraints on the server side with Reverse Ajax techniques.

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.



26 July 2011

Also available in Chinese Russian Japanese Vietnamese

Introduction

Today, 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.

In this article, learn how to implement Reverse Ajax using WebSockets. Code examples help illustrate WebSockets, FlashSockets, constraints on the server side, request-scoped services, and pausing long-lived requests. 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 the article, you will also need the latest version of Maven and the JDK (see Resources).


WebSockets

WebSockets, which emerged in HTML5, is a much more recent Reverse Ajax technique than Comet. WebSockets enables bi-directional, full-duplex communication channels, and many browsers (Firefox, Google Chrome, and Safari) already support it. The connection is opened through an HTTP request, called a WebSockets handshake, with some special headers. The connection is kept alive, and you can write and receive data in JavaScript as if you were using a raw TCP socket.

A WebSocket URL is started by typing ws:// or wss:// (on SSL).

The timeline in Figure 1 shows communication using WebSockets. An HTTP handshake is sent to the server with specific headers. Then, a type of socket is made available either on the server or on the client side in JavaScript. This socket can be used to asynchronously receive data through an event handler.

Figure 1. Reverse Ajax with WebSockets
Reverse Ajax with WebSockets

There is a WebSocket example in the downloadable source code for this article. When you run the example, you should see output similar to Listing 1. It shows how events occurred on the server side and immediately appeared on the client side as well. When the client sends some data, the server echoes it back to the client.

Listing 1. WebSocket sample in JavaScript
[client] WebSocket connection opened 
[server] 1 events 
[event] ClientID = 0 
[server] 1 events 
[event] At Fri Jun 17 21:12:01 EDT 2011 
[server] 1 events 
[event] From 0 : qqq 
[server] 1 events 
[event] At Fri Jun 17 21:12:05 EDT 2011 
[server] 1 events 
[event] From 0 : vv

Typically, in JavaScript you would use WebSockets as demonstrated in Listing 2, if your browser supports it.

Listing 2. JavaScript client code
var ws = new WebSocket('ws://127.0.0.1:8080/async'); 
ws.onopen = function() { 
    // called when connection is opened 
}; 
ws.onerror = function(e) { 
    // called in case of error, when connection is broken in example 
}; 
ws.onclose = function() { 
    // called when connexion is closed 
}; 
ws.onmessage = function(msg) { 
    // called when the server sends a message to the client. 
    // msg.data contains the message. 
}; 
// Here is how to send some data to the server 
ws.send('some data'); 
// To close the socket:
ws.close();

The data that is sent and received can be of any type. WebSockets can be viewed like TCP sockets, so it's up to the client and server to know which type of data is being sent through. The example here is sending JSON strings.

When the JavaScript WebSocket object is created, you should see the WebSocket-specific headers if you look deeper in the HTTP requests in your browser's console (or Firebug) for the handshake. Listing 3 shows an example.

Listing 3. Sample HTTP request and response headers
Request URL:ws://127.0.0.1:8080/async 
Request Method:GET 
Status Code:101 WebSocket Protocol Handshake 

Request Headers 
Connection:Upgrade 
Host:127.0.0.1:8080 
Origin:http://localhost:8080 
Sec-WebSocket-Key1:1 &1~ 33188Yd]r8dp W75q 
Sec-WebSocket-Key2:1   7;    229 *043M 8 
Upgrade:WebSocket 
(Key3):B4:BB:20:37:45:3F:BC:C7 

Response Headers 
Connection:Upgrade 
Sec-WebSocket-Location:ws://127.0.0.1:8080/async 
Sec-WebSocket-Origin:http://localhost:8080 
Upgrade:WebSocket 
(Challenge Response):AC:23:A5:7E:5D:E5:04:6A:B5:F8:CC:E7:AB:6D:1A:39

All of the headers are used by the WebSocket handshake to authorize and set up the long-lived connection. The WebSocket JavaScript object also contains two useful properties:

ws.url
Returns the URL of the WebSocket server.
ws.readyState
Returns a value of the current connection state:
  • CONNECTING = 0
  • OPEN = 1
  • CLOSED = 2

On the server side, handling WebSockets is a little more complicated. There is not (yet) a Java specification to support WebSockets in a standard way. To use the WebSockets features of the web container (for example, Tomcat or Jetty), you have to tightly couple your application code to the container-specific library that enables you to access the WebSockets feature.

The example found in the websocket folder of the sample code uses the Jetty's WebSocket API since we're using a Jetty container. Listing 4 shows the WebSocket handler. (Part 3 of this series will use different back-end WebSocket APIs.)

Listing 4. WebSocket handler for a Jetty container
public final class ReverseAjaxServlet extends WebSocketServlet { 
    @Override 
    protected WebSocket doWebSocketConnect(HttpServletRequest request,
                                           String protocol) { 
        return [...] 
    } 
}

With Jetty, there are several ways to handle a WebSocket handshake. The easier way is to subclass Jetty's WebSocketServlet and implement the doWebSocketConnect method. This method asks you to return an instance of the Jetty's WebSocket interface. You have to implement the interface and return a sort of endpoint representing the WebSocket connection. Listing 5 provides a sample.

Listing 5. WebSocket implementation sample
class Endpoint implements WebSocket { 

    Outbound outbound; 

    @Override 
    public void onConnect(Outbound outbound) { 
        this.outbound = outbound;    
    } 

    @Override 
    public void onMessage(byte opcode, String data) { 
        // called when a message is received 
        // you usually use this method 
    } 

    @Override 
    public void onFragment(boolean more, byte opcode, 
                           byte[] data, int offset, int length) { 
        // when a fragment is completed, onMessage is called. 
        // Usually leave this method empty. 
    } 

    @Override 
    public void onMessage(byte opcode, byte[] data, 
                          int offset, int length) { 
        onMessage(opcode, new String(data, offset, length)); 
    } 

    @Override 
    public void onDisconnect() { 
        outbound = null; 
    } 
}

To send a message to the client, you write to the outbound, as shown in Listing 6:

Listing 6. Sending a message to the client
if (outbound != null && outbound.isOpen()) {
    outbound.sendMessage('Hello World !');
}

To disconnect the client and close the WebSocket connection, use outbound.disconnect();.

WebSockets is a very powerful way to implement a bi-directional communication with no latency. It is supported by Firefox, Google Chrome, Opera, and other modern browsers. According to the jWebSocket website:

  • Chrome includes native WebSockets since 4.0.249.
  • Safari 5.x includes native WebSockets.
  • Firefox 3.7a6 and 4.0b1+ includes native WebSockets.
  • Opera includes native WebSockets since 10.7.9067.

For more information about jWebSocket, see Resources.

Advantages

WebSockets provides powerful, bi-directional, low-latency, and easy-to-handle errors. There isn't a lot of connection, like Comet long polling, and it doesn't have the drawbacks of Comet streaming. The API is also very easy to use directly without any additional layers, compared to Comet, which requires a good library to handle reconnection, timeout, Ajax requests, acknowledgments, and the optionally different transports (Ajax long polling and jsonp polling).

Drawbacks

Drawbacks of WebSockets include:

  • It is a new specification from HTML5, so it isn't yet supported by all browsers.
  • No request scope. Since WebSockets is a TCP socket and not an HTTP request, request-scoped services, like Hibernate's SessionInViewFilter, cannot be used easily. Hibernate is a persistence framework that provides a filter to surround an HTTP request. When the request begins, it sets up a contest (containing transactions and JDBC connection) bound to the request thread. When the request finishes, the filter destroys this contest.

FlashSockets

For browsers that don't support WebSockets, some libraries have the ability to fall back to FlashSockets (sockets through Flash). The libraries usually provide the same official WebSocket API, but they implement it by delegating calls to a hidden Flash component included on the website.

Advantages

FlashSockets transparently provide the WebSockets feature, even on browsers that do not support HTML5 WebSockets.

Drawbacks

FlashSockets has the following drawbacks:

  • It requires installation of the Flash plug-in (usually, all browsers have it).
  • It requires that the port 843 of the firewall is opened so the Flash component can make an HTTP request to retrieve a policy file containing domain authorization.

    If the port 843 is unreachable, the library should fall back or give an error. All this processing takes time (up to 3 seconds, depending on the library), which will slow down the website.

  • If the client is behind a proxy server, the connection to port 843 might be refused.

The WebSocketJS project provides a bridge. It requires at least Flash 10 and brings WebSockets support to Firefox 3, Internet Explorer 8, and Internet Explorer 9.

Recommendations

Compared to Comet, WebSockets brings many benefits. In day-to-day development, clients supporting WebSockets are faster and generate fewer requests (and thus consume less bandwidth). But, since not all browsers are supporting WebSockets, the best choice for a Reverse Ajax library would be one that's capable of detecting WebSockets support and falling back to Comet (long polling) if WebSockets are not supported.

Since the two techniques are necessary to get the best of all browsers and remain compatible, it is recommended that you use a client JavaScript library that provides a layer of abstraction on top of these techniques. Part 3 and Part 4 of this series will explore some libraries, and Part 5 will show their application. On the server side, things are a little more complicated, as discussed in the next section.


Reverse Ajax constraints on the server side

Now that you have an overview of the Reverse Ajax solutions available on the client side, let's look at Reverse Ajax solutions on the server. Up to now, the examples have used mainly client JavaScript code. On the server side, to accept Reverse Ajax connections, some techniques require specific features to handle long-lived connections compared to the short HTTP requests you're familiar with. For better scaling, a new threading model should be used that requires a specific API in Java to be able to pause requests. Also, for WebSockets, you have to correctly manage the scope of the services used in the application.

Threading and non-blocking I/O

Usually, a web server associates one thread, or one process, per incoming HTTP connection. This connection can be persistent (keep-alive) so that several requests go through the same connection. In this article's example, the Apache web server can be configured with the mpm_fork or mpm_worker models to change this behavior. Java web servers (application servers are included—this is the same thing) typically use one thread for each incoming connection.

Spawning a new thread leads to memory consumption and resource wasting because it's not guaranteed that the spawned thread will be used. The connection may be up, but no data from either the client or the server is sent. Whether this thread is used or not, it consumes memory and CPU resources for scheduling and contest switches. And, when configuring a server using a threading model, you usually have to configure a thread pool (set a maximum number of threads to process incoming connections). If this value is misconfigured and is too low, you'll end up with a thread starvation issue; requests will wait until a thread is available to process them. Response time will be slow when the maximum concurrent connection is reached. On the other hand, configuring a high value may lead to an out of memory exception. The spawning of too many threads would consume all the heap size of the JVM and lead to a server crash.

Java recently introduced a new I/O API called a non-blocking I/O. This API uses a selector that avoids binding a thread each time a new HTTP connection is made to the server. When data is coming, an event is received and a thread is allocated to process the request. Thus, this is called a thread-per-request model. It allows web servers, such as WebSphere and Jetty, to scale and handle a growing number of user connections with a fixed number of threads. With the same hardware configuration, web servers running in this mode scale much better than in the thread-per-connection mode.

In his blog, Philip McCarthy (author of Comet and Reverse Ajax) has an interesting benchmark about the scalability of the two threading models (see Resources for a link). In Figure 2 you'll find the same pattern: a threading model stops working with too many connections.

Figure 2. Benchmark of threading models
Benchmark of threading models

The thread-per-connection model (Threads in Figure 2) typically has a better response time, since all threads are up, ready, and waiting, but it stops serving when the connection number is too high. In the thread-per-request model (Continuations in Figure 2), a thread is used to serve the arrived request, and the connection is handled through an NIO selector. The response time may be a little slower, but threads are recycled and thus this solution scales better with a lot of connections.

To understand how threading works behind the scenes, imagine a LEGO™ block as the selector. Each incoming connection arrives in this LEGO block and is identified by a pin. The LEGO block/selector will have as many pins (as many keys) as connections. Then, only one thread is necessary to iterate over the pins as it waits for new events to happen. When something happens, the selector thread retrieves the keys for the events that occurred and a thread can be used to serve the incoming request.

The "Rox Java NIO Tutorial" has a good example of using NIO in Java (see Resources).


Request-scoped services

Many frameworks provide services, or filters, that handle a web request arriving into a servlet. For example, a filter will:

  • Bind a JDBC connection to a request thread so only one connection is used for the whole request.
  • Commit the changes at the end of the request.

Another example is the Guice Servlet extension of Google Guice (a dependency injection library). Like Spring, Guice can bind services in a request scope. An instance will be created once, at most, for each new request (see Resources for more information).

Typical usage would involve caching a user object retrieved from a repository (for example, a database) in the request by using the user id taken from the clustered HTTP session. In Google Guice, you might have code similar to Listing 7.

Listing 7. Request-scoped binding
@Provides 
@RequestScoped 
Member member(AuthManager authManager, 
              MemberRepository memberRepository) { 
    return memberRepository.findById(authManager.getCurrentUserId());
}

When a member gets injected in a class, Guice will try to fetch it from the request. If not found, it will execute the repository call and place the result in the request.

Request-scoped services can be used with any Reverse Ajax solution except for WebSockets. Any other solution relies on HTTP requests, either short or long-lived, so each request goes through the servlet dispatching system and filters are executed. When completing a paused (long-lived) HTTP request, you'll see in subsequent parts of this series that there's also an option to make the request go through the filter chain again.

For WebSockets, data arrives directly on the onMessage callback, like in a TCP socket. There has not been any HTTP request sent for this data to arrive, so there is no request contest from which to get and store scoped objects. Thus, using services requiring scoped objects from an onMessage callback will fail.

The guice-and-websocket sample in the downloadable source code shows how to bypass the limitation and still use request-scoped objects in an onMessage callback. When you run the sample and click each button on the web page to test an Ajax call (request-scoped), a WebSocket call, and a WebSocket call with a simulated request scoped, you will get the output shown in Figure 3.

Figure 3. Output of a WebSocket handler using request-scoped services
Output of a WebSocket handler using request-scoped services

You might encounter such issues whether you're using:

  • Spring.
  • Hibernate.
  • Any other framework requiring request scoped or a per-request model, such as OpenSessionInViewFilter.
  • Any system using the ThreadLocal facility to scope variables to a request thread within a filter and access them later.

Guice has an elegant resolution, as shown in Listing 8:

Listing 8. Simulate a request-scope from the WebSocket onMessage callback
// The reference to the request is hold when the 
// doWebSocketMethod is called 
HttpServletRequest request = [...] 
Map<Key<?>, Object> bindings = new HashMap<Key<?>, Object>(); 
// I have a service which needs a request to get the session, 
// so I provide the request, but you could provide any other 
// binding that may be needed 
bindings.put(Key.get(HttpServletRequest.class), request); 
ServletScopes.scopeRequest(new Callable<Object>() { 
    @Override 
    public Object call() throws Exception { 
        // call your repository or any service using the scoped objects
        outbound.sendMessage([...]); 
        return null; 
    } 
}, bindings).call();

Pausing long-lived requests

With Comet, there is another hurdle. How can a server pause a long-lived request without impacting performance, and then recover and complete it as soon as a server event comes?

Obviously, you can't simply hold the request and response, which could cause thread starvation and high memory consumption. Pausing a long-polling request, among non-blcoking I/O, requires a specific API. In Java, the Servlet 3.0 specification provides such an API (see Part 1 of this series). Listing 9 shows an example.

Listing 9. Defining an asynchronous servlet with Servlet 3.0
<?xml version="1.0" encoding="UTF-8"?> 

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
         xmlns:j2ee="http://java.sun.com/xml/ns/javaee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml
/ns/j2ee/web-app_3.0.xsd"> 

    <servlet> 
        <servlet-name>events</servlet-name> 
        <servlet-class>ReverseAjaxServlet</servlet-class> 
        <async-supported>true</async-supported> 
    </servlet> 

    <servlet-mapping> 
        <servlet-name>events</servlet-name> 
        <url-pattern>/ajax</url-pattern> 
    </servlet-mapping> 

</web-app>

When you've defined an asynchronous servlet, you can use the Servlet 3.0 API to suspend and resume a request, as shown in Listing 10:

Listing 10. Suspending and resuming a request
AsyncContext asyncContext = req.startAsync(); 
// Hold the asyncContext reference somewhere

// Then when needed, in another thread you can resume or complete
HttpServletResponse req = 
    (HttpServletResponse) asyncContext.getResponse(); 
req.getWriter().write("data"); 
req.setContentType([...]); 
asyncContext.complete();

Prior to Servlet 3.0, each container had, and still has, its own mechanism. Jetty's continuations is a well-known example; many Reverse Ajax libraries in Java depend upon Jetty's continuations. This is not a show-stopper and does not require that you run your application in a Jetty container. The API is clever enough to detect the container you're running and fall back to the Servlet 3.0 API, if available, when run in another container such as Tomcat or Grizzly. This is true for Comet, but if you want to take advantage of WebSockets, you currently have no choice but to use the container-specific features.

The Servlet 3.0 specification is not yet released, but a lot of containers are already implementing the API since it is a standard way of doing Reverse Ajax.


Conclusion

WebSockets is a very powerful Reverse Ajax solution, despite a few drawbacks. It's not currently implemented on all browsers, and isn't easy to use on the server side in Java without the help of a Reverse Ajax library. Because you aren't using a standard request-response style, you cannot rely on the filter chain execution for scopes. Comet and WebSockets require server-side-specific features of the containers, so you need to pay attention when using a recent container or it will not scale.

Stay tuned for Part 3 of this series, which will explore the different APIs on the server side for Comet and WebSockets. Also learn about Atmosphere, a Reverse Ajax framework.


Download

DescriptionNameSize
Article source codereverse_ajaxpt2_source.zip14KB

Resources

Learn

Get products and technologies

  • WebSocketJS (WebSocket Flash Bridge): Get this HTML5 WebSocket implementation powered by Flash.
  • Google Guice: Get Google Guice, a lightweight dependency injection framework for Java 5 and above.
  • Jetty: Get Jetty, a web server and javax.servlet container, plus support for WebSockets.
  • 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=743078
ArticleTitle=Reverse Ajax, Part 2: WebSockets
publish-date=07262011