Archive

Archive for the ‘Comet’ Category

Atmosphere 1.0, the Asynchronous JavaScript/Java Framework now available!

September 4, 2012 36 comments

Today, after almost 4 years of in and out works(*), I’m really happy to announce the immediate availability of the Atmosphere Framework version 1.0, the only Asynchronous JavaScript/Java Portable framework running on the JVM!

The Atmosphere Framework ships with multiple components, and can be used with JRuby, Scala, Groovy and Java:

  • Atmosphere Javascript: The client side of Atmosphere, supporting by all browsers. Supports Websockets and HTML 5 Server Sides Events when available, transparently fallback to Http Streaming/Long Polling or JSONP when not supported. Available for JQuery and Ext JS. Supports sharing/multiplexing a single connection amongst browser’s windows/tabs.
  • Atmosphere Runtime: The server side of Atmosphere, supporting all major WebServers. Supports WebSockets and HTML 5 Server Sides Events when available, fallback to WebServer’s Native Comet Implementation or the ugly Servlet 3 Async API at last. Also works transparently on top of the Netty and Grizzly Framework (no Servlet Container required). It also transparently brings WebSockets support to any exiting Servlet, without any changes required!
  • Atmosphere Jersey:  A native extension to the Jersey Framework (REST). Transparently brings WebSockets and HTML5 Server Side Events to  your REST architecture, using the JAX RS API. Also support the strange JAX RS 2 Async API.
  • Atmosphere GWT: A native extension to the GWT Framework. Transparently brings WebSockets and HTML5 Server Side Events.
  • Atmosphere Socket.IO: A native extension to the Socket.IO protocol. Free you from node.js, brings you the JVM scalabilty for free. WebSockets included.
  • Atmosphere Cometd: Run the Cometd Framework on top of Atmosphere, get portable WebSockets  and the cloud API for free.
  • Atmosphere Cloud API: Many module are available to make your application working into the cloud. Native Support for Redis PubSub, Hazelcast, JGroups, XMPP (GMail), JMS

Atmosphere is natively supported and available in PrimeFaces, Apache Wicket, Vaadin, Grails, RichFaces, Scalatra, Apache Tuscany, JQuery-Socket etc. and works fine with Spring and Google Guice.

Atmosphere works fine with Firefox 3+, Internet Explorer 6+, Opera 10+, Chrome 2+, Safari 2+, IOS 2+ and Android 2+ and supports connections sharing across multiple tabs and windows as. Open one connection, share it on all pages!

Atmosphere works with every Java EE Application Server and can automatically negotiates the best transport between the client and server. Atmosphere’s support portable WebSockets with Jetty, GlassFish, Grizzly 2, Tomcat and Netty. Write once, deploy anywhere!

Atmosphere ships with many samples, so get started very fast.

In short, Atmosphere works everywhere and with every framework. Websockets, Server Sides Events or any HTTP techniques are supported transparently. Write one javascript, one server file and Atmosphere will make it work every where, without any changes! As simple as (Client)

    var request = { url: document.location.toString() + 'chat',
                    contentType : "application/json",
                    transport : 'websocket' ,
                    fallbackTransport: 'long-polling'};

    request.onOpen = function(response) {
         // Display the user logged
    };

    request.onMessage = function (response) {
        var message = response.responseBody;
        var json = jQuery.parseJSON(message);
        addMessage(json);
    };

(Server)

@AtmosphereHandlerService(path = "/chat", 
interceptors= {AtmosphereResourceLifecycleInterceptor.class})
public class Chat implements AtmosphereHandler {

    @Override
    public void onRequest(AtmosphereResource r) throws IOException {
        r.getBroadcaster().broadcast(r.getRequest().getReader().readLine());
    }

    @Override
    public void onStateChange(AtmosphereResourceEvent event) throws IOException {
        AtmosphereResponse res = r.getResponse();

        if (event.isSuspended()) {
            String body = event.getMessage().toString();
            res.getWriter().write(new Data(body).toString());
        }
    }
}

You can see the Wordnik’s Atmosphere implementation lives, servicing approximatively between 40 to 50 millions of requests per day on WSJ.com (depending on enabled pages), or with any website using our Related Content PlugIn.

Atmosphere gets more than 25 000 downloads per month and widely used in production. Follow us on Github or Twitter. The more we are the better it will be!

(*) I would like to gigantically thanks Tony Tam and Erin McKean (and the team @Wordnik) for allowing me to work a significant amount of my time on Atmosphere every day, since last year!!.

Categories: Atmosphere, Comet, JQuery, Websocket

Writing Socket.IO application that runs on the JVM

The Socket.IO framework is defined as “Socket.IO aims to make realtime apps possible in every browser and mobile device, blurring the differences between the different transport mechanisms.”. If you are familiar with the Atmosphere Framework, this sound like a perfect match: The Atmosphere Framework aims to make realtime apps possible in every JVM based-Server, every browser and mobile device blurring the differences between the different transport mechanisms.

The major difference is with Socket.IO you are “stuck with node.js”, where with Atmosphere you can pick up the JVM based server of your choice, and get the guarantee that your application will works AS-IT-IS with any of those servers. Socket.IO + node.js forever? Not anymore! This blog introduces Socket.IO for the JVM, or how to write Socket.IO application running on the JVM, using the Atmosphere Framework!

If you are coming from the Socket.IO community, you may want to take a look at this page to grab more information about Atmosphere. For this blog, I will use the usual chat example I always use to demonstrate how simple Atmosphere is. And more important, all existing Atmosphere Application will works with Socket.IO WITHOUT ANY CHANGE! That means you can easily throw away your atmosphere.js code and instead use the Socket.IO library instead, without having to change anything on the server side.

Now, let’s write a really simple chat, let’s use an AtmosphereHandler, which is defined as:

@AtmosphereHandlerService(path = "/chat",
interceptors = {AtmosphereResourceLifecycleInterceptor.class})
public class SocketIOChatAtmosphereHandler implements AtmosphereHandler {

    @Override
    public void onRequest(AtmosphereResource r) 
          throws IOException {
        r.getBroadcaster().broadcast(
            r.getRequest().getReader().readLine());
    }

    @Override
    public void onStateChange(AtmosphereResourceEvent event) throws IOException {
        AtmosphereResource r = event.getResource();
        if (event.isSuspended()) {
            // THIS IS JUST FOR DEMO, use JACKSON instead.
            String body = event.getMessage().toString();
            String author = body.substring(body.indexOf(":") + 2,
                 body.indexOf(",") - 1);
            String message = body.substring(body.lastIndexOf(":") + 2,
                 body.length() - 2);
            event.write(("{ \"text\" : \"" 
                    + text + "\", \"author\" : \"" 
                    + author + "\" , \"time\" : " 
                    + new Date().getTime() + "}").getBytes());
        }
    }

    @Override
    public void destroy() {
    }
}

Let’s make it clear, I could have used a JSON parser but for the sake of this blog I just want to make it really really simple. The idea here is all requests mapping the ‘chat’ pattern will be delegated to this AtmosphereHandler. Every time a broadcast operation happens (when the Socket.IO POST data), that data will be delivered to the AtmosphereHandler#onStateChange, which will write the data back to the Socket.IO client. If there is N connected client, the onStateChange will be asynchronously called N times, with the end result of sending N response back to Socket.IO. Note here that there is no special code for handling transport, e.g everything is all handled by Atmosphere. That means the above AtmosphereHandler TRANSPARENTLY supports WebSockets, HTML5 Server Side Events (SSE), HTTP long-polling, HTTP Streaming or the JSONP technique. The best transport will be picked by Atmosphere based on what the client and server supports. Of course if you like to write bare metal Socket.IO application, take a look at this class, which demonstrate the low level protocol support in Atmosphere.

Now we have the choice of WebServer, but for this blog I will use NettoSphere(Atmosphere running on top of the Netty Framework). You install SocketIOChatAtmosphereHandler by simply doing:

public class SocketIOServer {

    public static void main(String[] args) throws IOException {
        new Nettosphere.Builder().config(
                new Config.Builder()
                        .port(8080)
                        .host("127.0.0.1")
                        .resource(SocketIOChatAtmosphereHandler.class)
                        .build())
                .build().start();
    }   
}

That’s it, our Socket.IO server side components is done. Now let’s use the latest Socket.IO 0.9.6 library for the client. For fun let’s rewrite the atmosphere.js Chat client code, this time using Socket.IO:

$(function () {
    "use strict";

    var detect = $('#detect');
    var header = $('#header');
    var content = $('#content');
    var input = $('#input');
    var status = $('#status');
    var myName = false;
    var author = null;
    var logged = false;
    var socket = io.connect('', {'resource': 'chat'});

    socket.on('connect', function () {
        content.html($('<p>', { text: 'Atmosphere connected using ' 
             + this.socket.transport.name}));
        input.removeAttr('disabled').focus();
        status.text('Choose name:');

        $.each(this.socket.transports, function(index, item) {
            $("#transport").append(new Option(item, item));
        });
    });

    socket.on('chat message', msg);

    socket.on('disconnect', function () {
        content.html($('<p>', { text: 'Sorry, '
            + 'but there\'s some problem with your '
            + 'socket or the server is down' }));
    });

    socket.on('error', function (e) {
        content.html($('<p>', { text: 'Sorry, '
            + 'but there\'s some problem with your '
            + 'socket or the server is down' }));
    });

    input.keydown(function(e) {
        if (e.keyCode === 13) {
            var msg = $(this).val();

            // First message is always the author's name
            if (author == null) {
                author = msg;
            }

            socket.emit('chat message', 
                $.stringifyJSON({ author: author, message: msg }));
            $(this).val('');

            input.attr('disabled', 'disabled');
            if (myName === false) {
                myName = msg;
            }
        }
    });

    function message(msg) {
        try {
            var json = jQuery.parseJSON(msg);
        } catch (e) {
            console.log('This doesn\'t look like a valid JSON: '
                , message.data);
            return;
        }

        if (!logged) {
            logged = true;
            status.text(myName + ': ').css('color', 'blue');
            input.removeAttr('disabled').focus();
        } else {
            input.removeAttr('disabled');

            var me = json.author == author;
            addMessage(json.author, json.text, me ? 'blue' 
                   : 'black', new Date(json.time));
        }
    }

    function addMessage(author, message, color, datetime) {
        content.append('<p><span style="color:' + color 
            + '">' + author + '</span> @ ' +
            + (datetime.getHours() < 10 ? '0' 
            + datetime.getHours() : datetime.getHours()) + ':'
            + (datetime.getMinutes() < 10 ? '0' 
            + datetime.getMinutes() : datetime.getMinutes())
            + ': ' + message + '</p>');
    }
});

The key part is the socket.on(‘chat message’, message) which will be invoked every time a new chat message is published. That’s IT! Pretty simple, it is? Now, depending on the browser you are using, Websockets or Long-polling will be used, TRANSPARENTLY! You can browse the complete code here.

For more Atmosphere samples, go there and pick the best sample. To compare Socket.IO with Atmosphere.js, 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!

Categories: Atmosphere, Comet, Websocket

Writing Portable HTML5 Server Side Events Applications using the Atmosphere Framework

The Atmosphere Framework easily allow the creation of HTML5 Server Side Events (SSE). Better, any existing Servlet based application can add SSE support without any changes to their existing application.

HTML5 Server Side Events (SSE) are getting more and more adopted and support for it starts to appear. As an example,  the GlassFish Application Server recently added support for it,  the upcoming release of the Jersey Framework is also adding some sort of support, and framework like jQuery-Socket has sample supporting SSE as well. Both GlassFish and Jersey suffer major issues: First, you need to use non portable API to start using SSE (will only work in GlassFish or Jersey) and second, they expose special API to support SSE, which is a major mistake in my opinion. Just take a look at how simple it can be to implement SSE using the jQuery-Socket sample. Why would you use heavyweight API like GlassFish or Jersey to achieve something that simple? Not only that, but currently Internet Explorer isn’t supporting SSE, so if you use either use GlassFish or Jersey, your application will NOT WORK with Internet Exporer. Oups!!!

This is where Atmosphere comes to the rescue. With Atmosphere, you don’t have to add anything special to have SSE supported by your application. Event better, you can ask Atmosphere to fallback to another technique if SSE is not supported. As an example, you can ask for WebSocket or Long-Polling to be used when SSE is not available. Atmosphere will transparently fall back. On the server side, you don’t need to care about anything as Atmosphere will do it for you. As an example, let’s write a simple Chat application using Atmosphere and Jersey (but not the Jersey SSE implementation!).

First, let’s define a super simple Jersey Resource:

  1 @Path("/")
  2 public class ResourceChat {
  3 
  4     @Suspend(contentType = "application/json")
  5     @GET
  6     public String suspend() {
  7        return "";
  8     }
  9 
 10     @Broadcast(writeEntity = false)
 11     @POST
 12     @Produces("application/json")
 13     public Response broadcast(Message message) {
 14          return new Response(message.author, message.message);
 15     }
 16 }

The important code here is line 4, where the Atmosphere Suspend annotation is used to suspend to tell Atmosphere to not commit the response, e.g leave the connection open. Under the hood it means the Atmosphere native SSE implementation will be enabled and SSE message transparently handled. With line 10, we are telling Atmosphere to broadcast the message back the client to all suspended connections, or stated differently, to all our connected Browser, supporting SSE or not. This is important to not here that if the remote browser isn’t supporting SSE, a fallback transport will be used. For that sample let’s use long-polling, but if you are using Internet Explorer 10 we could have chosen WebSockets as a fallback mechanism. You can download the server code (complete sample) here.

No on the client side, all we need to do is to tell Atmosphere to use SSE as its main transport (complete code here):

  1 var request = { url: document.location.toString() + 'chat',
  2     contentType : "application/json",
  3     logLevel : 'debug',
  4     transport : 'sse' ,
  5     fallbackTransport: 'long-polling'};
  6 
  7     
  8 request.onOpen = function(response) {
  9     content.html($('<p>', { text: 'Atmosphere connected using '
          + response.transport }));
 10     input.removeAttr('disabled').focus();
 11     status.text('Choose name:');
 12 };
 13     
 14 request.onMessage = function (response) {
 15     var message = response.responseBody;
 16     try {
 17         var json = JSON.parse(message); 
 18     } catch (e) {
 19         console.log('This doesn\'t look like a valid JSON: ',
                    message.data);
 20         return;
 21     }
 22         
 23     if (!logged) {
 24         logged = true;
 25         status.text(myName + ': ').css('color', 'blue');
 26         input.removeAttr('disabled').focus();
 27     } else {
 28         input.removeAttr('disabled');
 29         
 30         var me = json.author == author;
 31         var date =  json.time;
 32         addMessage(json.author, json.text, me 
               ? 'blue' : 'black', new Date(date));
 33     }
 34 };
 35     
 36 request.onError = function(response) {
 37     content.html($('<p>', { text: 
           'Sorry, but there\'s some problem with your '
 38                 + 'socket or the server is down' }));
 39 };
 40                                                                                                                                                          
 41 var subSocket = socket.subscribe(request);

Important code here is line 1 where we create a request and configuring SSE as the main transport and Long-Polling as a fallback transport. Note that we could replace SSE with WebSocket and our application WILL STILL WORK, WITHOUT ANY CHANGES NEEDED ON THE SERVER SIDE!!! Line 8, 14 and 36 are used to define some function that will be invoked when the connection gets established, when messages are received and when network errors occurs. And your application will works everywhere Servlet 2.5 is supported. So the question is: Why using private/non portable API and worse, special API just to support only Server Side Events? Don’t go that route and use Atmosphere, and get all for free!

For any questions or to download Atmosphere Client and Server Framework, go to our main site, use our Google Group forum, follow the team or myself and tweet your questions there! .

Categories: Atmosphere, Comet, JQuery, Websocket

Websockets or Comet or Both? What’s supported in the Java EE land

In preparation for the Atmosphere Framework 1.0.0 release, I’ve started listing what Java EE (or WebServer) supports in terms of Native Comet and WebSockets. This blog describes who supports what. I’ve also added a section describing what transport (WebSocket, Http Streaming, Long Polling or JSONP) Browsers are supporting, again tested using Atmosphere. Of course, Atmosphere supports both matrix, but that’s not the goal of this blog. I’ve also not included SPDY or Server Side Events Support because they are yet to be implemented by the majority of Servers.

Server Supports

The following table describes what Atmosphere supports but it can also be seen as what available in Java EE in general. The third and fourth columns are describing if the server has native support for Comet and WebSocket, by either supporting the ugly Servlet 3.0 API or if they have native implementation. The same apply for WebSockets native support (there is no WebSocket standard yet). Last four columns list the “most” popular transport, e.g WebSockets, Http Streaming (also called Forever Frame), Long-Polling and JSSONP. Note that servers like Tomcat 5 doesn’t support native Comet or WebSocket, but Comet can always be emulated by blocking a thread (this is what Atmosphere is doing when deployed there).

(LP: long-Polling HS: Http Streaming)

Server Version Native Comet Native WebSocket WebSockets LP HS JSONP
Netty 3.3.x X X X X X X
Jetty 5.x X X X
Jetty 6.x X X X X
Jetty 7.x X X X X X X
Jetty 8.x X X X X X X
GlassFish 2.x X X X X
GlassFish 3.x to 3.1.1 X X X X
GlassFish 3.1.2 X X X X X X
Tomcat 5.x X X X
Tomcat 6.x X X X X
Tomcat 7.0.26 and lower X X X X
Tomcat 7.0.27 and up X X X X X X
JBoss 5.x X X X
JBoss 6.x X X X X
JBoss 7.x X X X X
WebLogic 10.x X X X X
WebLogic 11.x and up X X X X
Resin 2.x X X X
Resin 3.x X  X X X X
WebSphere 7.x X X X
WebSphere 8.x X X X

Supported Browsers

The current list of Browsers have been tested with Atmosphere using the atmosphere.js Javascript library and the transport they supports.

Browser Version WebSockets Long-Polling Http Streaming JSONP
Firefox 3.x to 8.x X X X
Firefox 9.x to 11.x X X X X
Chrome 12.x and lower X X X
Chrome 13.x and higher X X X X
Internet Explorer 6x to 9.x X X X
Internet Explorer 10.x X X X X
Opera 10.x and lower X X
Opera 11.x X X X
Safari 4.x X X X
Safari 5.x X X X X
Android 2.x and up X X X
Safari (iOS) 1.x to 4.x X X X
Safari (iOS) 5.x X X X X

Note that I haven’t listed private/commercial/proprietary WebSockets implementations like Kaazing and JWebSocket or Pusher or framework like Cometd (who only works properly on Jetty).

That’s it. If you are planning to use WebSocket and Java EE, I strongly recommend you look at Atmosphere instead of using private native API and get stuck on a server forever. For more information, ping me on Twitter!

Categories: Atmosphere, Comet, Websocket

Atmosphere .9 .9 .9 .9 released: Tomcat/GlassFish WebSocket, Netty Framework, Hazelcast, Fluid API, JQuery Optimization

The Atmosphere Framework version 0.9 has been released and contains a lot of new cool features and bugs fixes (around 101!!). This blog will describes the main new features! But first, let’s make it clear: the Atmosphere Framework has been designed to transparently support Comet and WebSocket with both client and server components. That means you don’t have to care if a server supports WebSocket or Comet, if a Browser supports WebSocket or not. You write your application and Atmosphere will pick the best transport for you, TRANSPARENTLY! As an example, all applications written with Atmosphere deployed on Jetty can now be deployed AS-IT-IS in Tomcat and automatically use the new Tomcat’s WebSocket API. Yes, you read it correctly: WITHOUT any changes!

Performance and Massive Scalability?

Atmosphere is live for 3 months of WSJ.com and Smartmoney.com and serves millions of requests using WebSocket, Long-Polling and JSONP => Atmosphere is production ready! And there is much more live Web Site using Atmosphere…I just can list them here.

Adoption

Atmosphere is available in mostly all available framework, either as a plugin or extension. If your framework isn’t supporting Atmosphere, complains to them and tell them it is really simple to add support!

jQuery.atmosphere.js new API

The Atmosphere Javascript client has been rewritten in order to allow a better integration with the WebSocket API  and auto detection of the best transport to use depending of server capacity. For example, atmosphere.js always try to use WebSocket as the initial transport and will transparently negotiate with the server to see which transport to use. If the server isn’t supporting WebSocket, atmosphere.js will transparently use HTTP (long-polling, streaming or JSONP). On both client and server side, your application doesn’t need to implement anything in order to work. API wise, function support has been added to make it really easy to write Javascript client. New functions available are onOpen, onReconnect, onClose, onError. As an example, a chat client supporting WebSocket and Comet will only consist of:

    var socket = $.atmosphere;
    var request = { url: document.location.toString() + 'chat',
                    contentType : "application/json",
                    logLevel : 'debug',
                    transport : 'websocket' ,
                    fallbackTransport: 'long-polling'};

    request.onOpen = function(response) {
       ...
    };

    request.onReconnect = function (request, response) {
       ...
    };

    request.onMessage = function (response) {
        var message = response.responseBody;
        ...
    };

    request.onError = function(response) {
       ...
    };

    var subSocket = socket.subscribe(request);

A lot of improvements has been made to hide Browser specific implementation: the famous Internet Explorer works transparently and supported version are 6/7/8/9 and always pick the best transport.

Native Tomcat WebSocket

Starting with Tomat 7.0.27, Atmosphere will detect the new Tomcat WebSocket API and use it by default. That means atmosphere.js will negotiate the WebSocket protocol and use it. That also means as soon as you deploy your application from previous Tomcat version to 7.0.27, WebSocket will transparently used. As an example, the chat client described above will transparently communicates using WebSocket to its associated Server component

@Path("/")
@Produces("application/json")
public class ResourceChat {

    @Suspend
    @GET
    public String suspend() {
        return "";
    }

    @Broadcast(writeEntity = false)
    @POST
    public Response broadcast(Message message) {
        return new Response(message.author, message.message);
    }
}

Now you can compare the number of line the Atmosphere Chat (support all transports) requires versus the Tomcat Chat (which, btw, only support WebSocket). Much more simpler with Atmosphere!

Netty Framework Support

YES, you read it correctly. Atmosphere has been refactored and can now be run on top of non Servlet Container! NettoSphere is the runtime that allow any existing Atmosphere application to run transparently on top of the Netty Framework. NettoSphere also support WebSocket and any existing applications will work without any change. As simple as

    Nettosphere server = new Nettosphere.Builder().config(
                 new Config.Builder()
                    .host("127.0.0.1")
                    .port(8080)
                    .staticResourcePath("/Users/jfarcand/")
                    .resource(MyResource.class)
                    .build())
                 .build();
    server.start();

GlassFish 3.1.2 WebSocket Support

The latest GlassFish release ships with an updated WebSocket implementation and Atmosphere makes use of it. As with Tomcat, Netty and Jetty, Atmosphere applications can be deployed without any modifications.

JAX-RS 2.0 Async API Support

The work in progress JAX-RS 2.0 specification introduces a new async API (which strangely looks like Atmosphere’s own API ;-)). The current incarnation of the API is really limited and I really hope the Spec EG will reconsider their decision. But whatever decision is made, Atmosphere supports the new annotation and the ExecutionContext class. The chat application would looks like

    // You can use that object to suspend as well.
    @Context ExecutionContext ctx;

    @Suspend() // Not the Atmosphere's Suspend
    @GET
    public String suspend() {
        // ctx.suspend
        return "";
    }

    @POST
    @Broadcast(writeEntity = false)
    public Response broadcast(Message message) {
        return new Response(message.author, message.message);
    }

You can compare with the current JAX-RS samples Chat, that only support long-polling (much more complex). The JAX-RS Async API lack of listener is a major issue IMO. As an example, Atmosphere’s Suspend annotation suppors event listeners which can be used to track the current state of the connection. As an example, with the current incantation of JAX-RS Async API, an application cannot be notified when a client drop the connection and hence the application’s resources can never be cleaned. That can easily produces out of memory error. Another problem is with the JAX-RS Async API it is quite complex to implement http-streaming because some browsers required some padding data before the response (WebKit and IE). In conclusion: since Atmosphere runs transparently in all WebServer, there are no good reasons to move to JAX RS 2 Async API (which will only runs on top of Servlet 3.0 WebServer). Instead use the API with Atmosphere and get portability, WebSocket and transport negotiation.

Native WebSocket Application

Atmosphere supports native WebSocket development, e.g application that only support WebSocket. A simple pubsub WebSocket will looks like

public class WebSocketPubSub extends WebSocketHandler {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketPubSub.class);

    @Override
    public void onTextMessage(WebSocket webSocket, String message) {
        AtmosphereResource r = webSocket.resource();
        Broadcaster b = lookupBroadcaster(r.getRequest().getPathInfo());

        if (message != null && message.indexOf("message") != -1) {
            b.broadcast(message.substring("message=".length()));
        }
    }

    @Override
    public void onOpen(WebSocket webSocket) {
        // Accept the handshake by suspending the response.
        AtmosphereResource r = webSocket.resource();
        Broadcaster b = lookupBroadcaster(r.getRequest().getPathInfo());
        r.setBroadcaster(b);
        r.addEventListener(new WebSocketEventListenerAdapter());
        r.suspend(-1);
    }

. The client would consists of

var request = { url : document.location.toString() };

request.onMessage = function (response) {
    if (response.status == 200) {
        var data = response.responseBody;
        if (data.length > 0) {
            // print the message
        }
    }
}
subSocket = socket.subscribe(request);

You can also use the WebSocket API directly, but atmosphere.js does a lot more (reconnection, proper API use, etc.)

Hazelcast support

Atmosphere can now be clustered/clouded using Hazelcast. As simple as:

@Path("/pubsub/{topic}")
@Produces("text/html;charset=ISO-8859-1")
public class JQueryPubSub {

    private @PathParam("topic") HazelcastBroadcaster topic;

    @GET
    public SuspendResponse subscribe() {
        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }

    @POST
    @Broadcast
    public Broadcastable publish(@FormParam("message") String message) {
        return new Broadcastable(message, "", topic);
    }
}

The broadcast operations will be distributed to all Hazelcast server available. You can masively scale your WebSocket application by just using the Hazelcast plugin! Don’t like Hazelcast? You can do the same using Redis/JMS/XMPP or JGroup broadcaster as well.

Miscellaneous

Tons of new features and improvements (101 issues fixed). Amongst them

1.0.0: Coming SOON, SwaggerSocket around the corner!

Atmosphere is fully powered by Wordnik and we are working hard to make the 1.0.0 release happens soon (End of May 2012). At Wordnik we are using Atmosphere a lot and soon we will release our REST over WebSocket protocol (called SwaggerSocket, which integrate with Swagger). New features will includes an Atmosphere Java Client that will mimic the Javascript functionality, Terracotta supports, etc (see a detailed list here).

For any questions or to download Atmosphere Client and Server Framework, go to our main site, use our Google Group forumfollow the team or myself and tweet your questions there! .

Atmosphere 0.8: Jersey on Steroid, WebSocket Sub Protocol, Native WebSocket, JQuery Plugin CORS, REST over HTTP

November 25, 2011 5 comments

The Atmosphere Framework 0.8 version has been released. This is our biggest release ever! This blog will describe the new feature covered by this fantastic release.

Jersey on Steroid with WebSocket

The Jersey Framework can now be fully run on top of a WebSocket connection seamlessly. A single WebSocket connection can now be used to invoke Jersey. That’s open the door to request pipeline and asynchronous processing of REST call transparently. This will be discussed soon in more details, but this feature is implemented via the new WebSocketProtocol API.

Browser WebSocket Support

Atmosphere fully support Opera, Firefox (MozWebSocket), Chrome, Safari and IE 10 Preview WebSocket transparently when used with the Atmosphere JQuery Plug In. Safari on iOS is also supported.

Native WebSocket Support

It is now possible to write native WebSocket application. A simple PubSub application can consist only of:

    public AtmosphereRequest onMessage(WebSocket webSocket, String message) {
        AtmosphereResource r = (AtmosphereResource) webSocket.resource();
        Broadcaster b = lookupBroadcaster(r.getRequest().getPathInfo());

        b.broadcast(message);

        //Do not dispatch to another Container
        return null;
    }

    public void onOpen(WebSocket webSocket) {
        // Accept the handshake by suspending the response.
        AtmosphereResource r = (AtmosphereResource) webSocket.resource();
        Broadcaster b = lookupBroadcaster(r.getRequest().getPathInfo());
        r.setBroadcaster(b);
        r.addEventListener(new WebSocketEventListenerAdapter());

        // Keep Alive the WebSocket Connection Forever
        r.suspend(-1);
    }

    public void onClose(WebSocket webSocket) {
        webSocket.resource().resume();
    }

    public void onError(WebSocket webSocket,
                        WebSocketProcessor.WebSocketException t) {
        logger.error(t.getMessage() + " Status {} Message {}",
                     t.response().getStatus(),
                     t.response().getStatusMessage());
    }

WebSocket Sub Protocol Implementation Support

Writing WebSocket sub protocol on top of a WebSocket connection is now extremely simple, thanks to the WebSocketProtocol API

public interface WebSocketProtocol extends AsyncProtocol{

    /**
     * Allow an implementation to query the
     * AtmosphereConfig of init-param, etc.
     */
    void configure(AtmosphereServlet.AtmosphereConfig config);

    /**
     * Parse the WebSocket message, and delegate the processing
     * to the {@link org.atmosphere.cpr.AtmosphereServlet#cometSupport} or
     * to any existing technology. Invoking
     * {@link org.atmosphere.cpr.AtmosphereServlet#cometSupport} will
     * delegate the request processing
     * to the {@link org.atmosphere.cpr.AtmosphereHandler} implementation.
     * Returning null means this implementation will
     * handle itself the processing/dispatching of the WebSocket's request;
     * /
    AtmosphereRequest onMessage(WebSocket webSocket, String data);

    AtmosphereRequest onMessage(WebSocket webSocket, byte[] data, int offset, int length);

    /**
     * Invoked when a WebSocket is opened
     * @param webSocket {@link WebSocket}
     */
    void onOpen(WebSocket webSocket);

    /**
     * Invoked when a WebSocket is closed
     * @param webSocket {@link WebSocket}
     */
    void onClose(WebSocket webSocket);

    /**
     * Invoked when an error occurs.
     * @param webSocket {@link WebSocket}
     * @param t a WebSocketProcessor.WebSocketException
     */
    void onError(WebSocket webSocket, WebSocketProcessor.WebSocketException t);

By default, Atmosphere uses the SimpleHttpProtocol to dispatch WebSocket message to Servlet based container. As an example, Atmosphere dispatch WebSockets messages to Jersey by wrapping the message inside an HttpServletRequest and  an asynchronous I/O HttpServletResponse. By default message are considered as POST when dispatched to Jersey, but all HTTP property are configurable (content-type, headers, cookies, etc.). Another example is the EchoProtocol, which just echo message to all connected WebSocket.

Improved Cross-Origin Resource Sharing (CORS) Support

The Atmosphere JQuery Plug In has an improved support for CORS, specially with IE 8/9. You can either turn it on globally or per request. As simple as:

                    jQuery.atmosphere.subscribe(
                        this.url,
                        this.atmosphereCallback,
                        jQuery.atmosphere.request = {
                            method : 'POST',
                            data : json,
                            transport: "websocket" ,
                            fallbackMethod: "POST",
                            enableXDR : 'true', 
 attachHeadersAsQueryString: true });

Trackability Support and Multi Tab

Trackability of remote AtmosphereResource was available in previous version only with Jersey. With 0.8, Trackability is now available to all modules and natively implemented in the JQuery Atmosphere Plug In. When the Plug In execute a request, a unique ID is assigned to the request. The server will read that unique id and will try to look up an AtmosphereResource linked to that ID. That allow application to remotely manipulate AtmosphereResource without the need to keep track, inside the application itself, a list of AtmosphereResource. That allow an application to suspend more than one AtmosphereResource per connection, opening the possibility to implement Browser multi-tab support by assigning a unique ID per tab, represented by different AtmosphereResource on the server side.

Atmosphere also support Trackable injection like:

  @Path("/subscribe")
  @POST def subscribeAndPublish(
    @HeaderParam(X_ATMOSPHERE_TRACKING_ID) trackedResource: 
                    TrackableResource[AtmosphereResource[_, _]],
    @HeaderParam(X_ATMOSPHERE_TRACKING_ID) trackingID: String,
    @HeaderParam(X_ATMOSPHERE_TRANSPORT) transport: String, message: String)
            : SuspendResponse[TrackableResource[AtmosphereResource[_, _]]] = 
    {
     ....
    }

Headers as Query String

Some environment are just allowing the GET operation. An example if the WebSocket Handshake operation, which by default execute a GET without allowing the client to configure any headers. IE CORS also only support this model. The good news is Atmosphere can encode the headers as a QueryString and decode it on the server side as header, allowing application to pass information during the handshake operation. As simple as:

                    jQuery.atmosphere.subscribe(
                        this.url,
                        this.atmosphereCallback,
                        jQuery.atmosphere.request = {
                            method : 'POST',
                            data : json,
                            transport: "websocket" ,
                            fallbackMethod: "POST",
                            attachHeadersAsQueryString: true

                        });

There is much more new features, take a look at the changes log for more info.

For any questions or to download Atmosphere Client and Server Framework, go to our main site, use our Google Group forum, follow the team or myself and tweet your questions there! .

Categories: Atmosphere, Comet, JQuery, Websocket

Hitchiker Guide to the Atmosphere Framework using WebSocket, Long-Polling and Http Streaming

November 7, 2011 4 comments

The Atmosphere Framework easily allow the writing of web application that support, transparently, SSE (Server Side Events), JSONP, WebSocket, Long-Polling and Http Streaming. The Atmosphere Framework also hide the complexity of the current asynchronous API, which differ from Server to Server and make your application portable among them. More important, it is much more easy to write an Atmosphere application than using the Servlet 3.0 API.

There are several APIs available in Atmosphere to write an asynchronous application: AtmosphereHandler, Meteor or using Jersey‘s Atmosphere extension. In this blog I will take the famous JQuery PubSub sample to demonstrate those APIs. Note that I will not discuss the JQuery Atmosphere Plugin as it is the same for all APIs. Important, all code snippet below support WebSocket, SSE, JSONP, Long-Polling and Streaming by default. Only the last section only support WebSocket.

The JQuery PubSub Application is quite simple. You enter a topic to subscribe, you select a transport to use (WebSocket, JSONP, SSE, Streaming or Long-Polling) or let the plug in decide for you, and then you are ready to publish message. You can use Redis on the server side to cluster your application among servers. The subscribe operation is done using a GET, the publish using a POST (in the form of message=”something”). If the WebSocket transport is used, the message is wrapped as a POST and delivered as a normal HTTP request. Note that this feature is configurable in Atmosphere

PubSub using AtmosphereHandler

The AtmosphereHandler  is a low level API that can be used to write an asynchronous application. An application just have to implement that interface. This API is usually used by other framework in order to integrate with Atmosphere (GWT, Jersey, Vaading, etc.) but it can also be used if you want to write Servlet style code.  So, with an AtmosphereHandler, the PubSub implementation will take the form of:

public class AtmosphereHandlerPubSub
      extends AbstractReflectorAtmosphereHandler {

    @Override
    public void onRequest
       (AtmosphereResource r)
          throws IOException {

        HttpServletRequest req = r.getRequest();
        HttpServletResponse res = r.getResponse();
        String method = req.getMethod();

        // Suspend the response.
        if ("GET".equalsIgnoreCase(method)) {
            String trackingId = trackingId(req);

            // Log all events on the console, including WebSocket events.
            r.addEventListener(new WebSocketEventListenerAdapter());

            res.setContentType("text/html;charset=ISO-8859-1");

            Broadcaster b = lookupBroadcaster(req.getPathInfo());
            r.setBroadcaster(b);

            if (req.getHeader(X_ATMOSPHERE_TRANSPORT)
                    .equalsIgnoreCase(LONG_POLLING_TRANSPORT)) {
                req.setAttribute(RESUME_ON_BROADCAST, Boolean.TRUE);
                r.suspend(-1, false);
            } else {
                r.suspend(-1);
            }
        } else if ("POST".equalsIgnoreCase(method)) {
            Broadcaster b = lookupBroadcaster(req.getPathInfo());

            String message = req.getReader().readLine();

            if (message != null && message.indexOf("message") != -1) {
                b.broadcast(message.substring("message=".length()));
            }
        }
    }

    @Override
    public void destroy() {
    }

    Broadcaster lookupBroadcaster(String pathInfo) {
        String[] decodedPath = pathInfo.split("/");
        Broadcaster b = BroadcasterFactory.getDefault()
              .lookup(decodedPath[decodedPath.length - 1], true);
        return b;
    }

}

When a GET is received, we lookup a Broadcaster and then suspend the response based on the path info (REST style). Here we need a make sure we aren’t sending padding data (required for WebKit browser) when long polling is used. That’s the only required conditional evaluation needed in terms of transport. With the POST we just look up the Broadcaster (which represent a pubsub topic) and broadcast the request’s body. That;s it.

PubSub using Meteor

The Meteor is another low level API that can be used with existing Servlet application. As an example, the ADF framework use Meteor in order to integrate Atmosphere support.

public class MeteorPubSub extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
       throws IOException {
        // Create a Meteor
        Meteor m = Meteor.build(req);

        // Log all events on the console, including WebSocket events.
        m.addListener(new WebSocketEventListenerAdapter());

        res.setContentType("text/html;charset=ISO-8859-1");

        Broadcaster b = lookupBroadcaster(req.getPathInfo());
        m.setBroadcaster(b);

        if (req.getHeader(X_ATMOSPHERE_TRANSPORT)
                .equalsIgnoreCase(LONG_POLLING_TRANSPORT)) {
            req.setAttribute(RESUME_ON_BROADCAST, Boolean.TRUE);
            m.suspend(-1, false);
        } else {
            m.suspend(-1);
        }
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws IOException {

        Broadcaster b = lookupBroadcaster(req.getPathInfo());
        String message = req.getReader().readLine();

        if (message != null && message.indexOf("message") != -1) {
            b.broadcast(message.substring("message=".length()));
        }
    }

    Broadcaster lookupBroadcaster(String pathInfo) {
        String[] decodedPath = pathInfo.split("/");
        Broadcaster b = BroadcasterFactory.getDefault()
              .lookup(decodedPath[decodedPath.length - 1], true);
        return b;
    }
}

When a GET is received, we create a Meteor and use that Meteor to suspend the response, again using the path info. For post, we do the same as with AtmosphereHandler, e.g retrieve the Broadcaster and broadcast the message.

PubSub using Jersey’s Atmosphere Extension

With the Jersey extension, we can either use annotations or the programmatic API. As simple as”

@Path("/pubsub/{topic}")
@Produces("text/html;charset=ISO-8859-1")
public class JQueryPubSub {

    private
    @PathParam("topic")
    Broadcaster topic;

    @GET
    public SuspendResponse subscribe() {
        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }

    @POST
    @Broadcast
    public Broadcastable publish(@FormParam("message") String message) {
        return new Broadcastable(message, "", topic);
    }
}

The GET could have been handled using the @Suspend annotation:

    @GET
    @Suspend(listeners = EventsLogger.class, outputComments = true)
    public Broadcastable subscribe(){
        return new Broadcastable(topic);
    }

As you can see, it is quite simpler that with Meteor and AtmosphereHandler.
PubSub.

PubSub using WebSocket only

If you are planning to write pure WebSocket application and don’t plan to support normal HTTP, you can also write your own WebSocket sub protocol. It is quote important to note here that only WebSocket will be supported.

public class WebSocketPubSub implements WebSocketProtocol {

    private AtmosphereResource r;

    @Override
    public HttpServletRequest onMessage(WebSocket webSocket, String message) {
        Broadcaster b = lookupBroadcaster(r.getRequest().getPathInfo());

        if (message != null && message.indexOf("message") != -1) {
            b.broadcast(message.substring("message=".length()));
        }

        //Do not dispatch to another Container like Jersey
        return null;
    }

    @Override
    public void onOpen(WebSocket webSocket) {
        // Accept the handshake by suspending the response.
        r = (AtmosphereResource)
              webSocket.atmosphereResource();

        Broadcaster b = lookupBroadcaster(r.getRequest().getPathInfo());
        r.setBroadcaster(b);
        r.addEventListener(new WebSocketEventListenerAdapter());

        r.suspend(-1);
    }

    @Override
    public void onClose(WebSocket webSocket) {
        webSocket.atmosphereResource().resume();
    }

    Broadcaster lookupBroadcaster(String pathInfo) {
        String[] decodedPath = pathInfo.split("/");
        Broadcaster b = BroadcasterFactory.getDefault().
                 lookup(decodedPath[decodedPath.length - 1], true);
        return b;
    }

The important method here is onOpen (for accepting the handshake) and the onMessage, which is were the messages are received.

Conclusion

It is quite important to pick the best API when writing Atmosphere application as it can save you a lot of time! You can download all samples from here.

For any questions or to download Atmosphere Client and Server Framework, go to our main site, use our Google Group forum, follow the team or myself and tweet your questions there! .

Categories: Atmosphere, Comet, JQuery, Websocket
Follow

Get every new post delivered to your Inbox.

Join 51 other followers