Archive

Archive for May 28, 2012

Writing Portable WebSockets Application using Java

Writing Portable WebSockets’ applications using the Java Language can be challenging. Since there is no API standardization yet, several “native” API exists (Play!, Vert.x, Kaazing), you can easily get locked with their API, changes, single vendor etc. The same “problem” happens with well known WebServer: Jetty, Tomcat or GlassFish all have native API making your application not portable. Worse, the same problem happens on the client side: Grizzly, Netty, JWebSocket all have their own API. Not better on the browser side as well: Firefox went with its own API instead of the recommended one (MozWebSocket, fixed in Firefox 12).

So, already giving up? The solution is simple: use the AHC library(client) and the Atmosphere Framework (browser and server). The AHC library adds a thin layer on top of existing WebSockets’ client libraries, using the Netty Framework as default. The Atmosphere Framework allow portable WebSockets across Java based WebServers.

Part I — Writing the Server Component

The Atmosphere Framework works on top of existing native WebSocket implementation and currently supports Jetty, Tomcat, GlassFish and the Netty Framework (the NettoSphere). The Atmosphere Framework allows writing pure WebSockets applications as well as applications supporting the HTML 5 Server Side Events, Long-Polling or HTTP Streaming. Today this blog will only focus on writing WebSockets-only applications. So, no fallback on another transport in case WebSockets aren’t supported, but it is important to note that Atmosphere transparently support that transport’s fallback.

The portable API and easiest way to write pure WebSockets with Atmosphere is called WebSocketHandler, and can be defined as:

public abstract class WebSocketHandler implements WebSocketProtocol {

    public void onByteMessage(WebSocket webSocket, 
                              byte[] data, int offset, int length) {
    }

    public void onTextMessage(WebSocket webSocket, String data) {
    }

    public void onOpen(WebSocket webSocket) {
    }

    public void onClose(WebSocket webSocket) {
    }

    @Override
    public void onError(WebSocket webSocket, 
                        WebSocketProcessor.WebSocketException t) {
    }

For example, let’s say we want to write a simple echo server that broadcast received messages to all connected WebSockets, all we need to do is to extends that class with:

    @Override
    public void onTextMessage(WebSocket webSocket, String message) {
        MetaBroadcaster.getDefault().broadcastTo("/*", messages);
    }

All this code is doing is to broadcast the received messages to all connected client using the MetaBroadcaster utility classes. Since, by default, all connected WebSockets are registered at ‘/*’, no extra code is required. Of course we could have wrote something more complicated (like writing our own WebSocket Protocol), but that’s not the goal of this blog.

Part II — Writing the Browser Component

For the Browser component, let’s use the Atmosphere JQuery Plugin, which supports all browsers and could fallback to another transport in case WebSocket aren’t supported. In the easiest form, all we need to do to receive WebSockets messages is:

   var socket = $.atmosphere;
   var req = new $.atmosphere.AtmosphereRequest();
   req.url = document.location.toString() + '/echo';
   req.transport = "websocket';

   req.onOpen = function(response) {
       alert("WebSocket opened");
   }

   req.onTransportFailure = function(request) {
       alert("This browser or the remote Server doesn't support WebSocket");
   }

   req.onMessage(response) {
      var message = response.responseBody;
      alert("WebSocket Message received: " + message);
   }

   var subSocket = socket.subscribe(request);

Of course a real application will not use the alert call. An interesting callback here is the onTransportFailure, which is called in case the browser or the server isn’t supporting WebSockets. For sending WebSocket’s message:

    subSocket.push("Hello World");

Part III — Writing a Java Client

Now let’s write a portable Java client using the AHC library. As simple as

    AsyncHttpClient c = new AsyncHttpClient();
    WebSocket w = c.prepareGet("ws://127.0.0.1:8080")
                   .execute(new WebSocketUpgradeHandler.Builder().build())
                  .get();
    w.addWebSocketListener(new WebSocketTextListener() {

       public void onMessage(String message) {
         System.out.println("Message Received: " + message);
       }

       public void onOpen(WebSocket websocket) {
         System.out.println("WebSocket Opened");
       }
   }).sendTextMessage("Hello World");

By default the AHC library use the Netty Framework for WebSockets support, but other framework (like the Grizzly Framework) can easily be replaced.

For a more complex, portable WebSockets application, take a look at the Atmosphere Chat sample, which transparently support WebSocket, Server Sides Events, Long-Polling and Streaming. If you are planning to use WebSockets and Java, I strongly recommend you look at Atmosphere instead of using private native API and get stuck on a server/framework forever. For more information, ping me on Twitter or follow the Atmosphere Framework!