Home > Atmosphere, Comet, Websocket > Writing Socket.IO application that runs on the JVM

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!

About these ads
Categories: Atmosphere, Comet, Websocket
  1. Joseph Fouad
    June 7, 2012 at 4:16 am

    Hi dear
    I agree with you that writing server side javascript code is a mess , and limited only to small applications , otherwise it will be full of spagetti code

    actually, right now i am writing a chat application backend , and i will appreciate your reply

    but, i wanna ask about atmosphere as i know very little about it

    nodejs can handle upto 10,000 “ten thousand” concurrent connection, that means it can manage 10,000 active socket without problem (i do not use that much, though)

    how does atmosphere handle multiple concurrent connections? does it use a thread per connection? if so , that limit the number of connections greatly….

    Does it use java nonblocking io ? java nio rely on the “select” system call, while nodejs use libev which use “epoll” on linux and “kqueue” on bsd which are much much more scalable than “select” , so nodejs can handle a much larger number of concurrent connection

    does this sounds right? please share your experience with me , as it will be very helpfull

    thanks a lot

    • June 7, 2012 at 8:20 pm

      Salut,

      Atmosphere use under the hood the async I/O API of the WebServer it runs on top of. As an example, deploying Atmosphere on Jetty can scale really high and doesn’t block a thread per connection because it is using NIO under the hood.

      BTW NIO use epoll under the hood when available.

      Thanks

      –Jeanfrancois

  2. June 18, 2012 at 2:09 am

    Now give me a way to do non-blocking file I/O (i.e. I get a callback when the OS has some data for me) and you might have something. NIO is a wonderful thing – but when your amazingly scalable single-threaded server calls a library that does synchronous I/O, it’s all over. I don’t see anybody addressing the fact that async is only useful for non-toy apps if the entire stack is async.

  3. b0c1
    June 22, 2012 at 12:16 pm

    Hi!

    I try to use Weblogic 12c with Atmosphere but I always get:

    java.lang.IllegalStateException: The async-support is disabled on this request: weblogic.servlet.internal.ServletRequestImpl@19c1438[

    top of the trace:

    at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1830)
    at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1806)
    at org.atmosphere.cpr.AtmosphereRequest.startAsync(AtmosphereRequest.java:527)

    I miss something?

  4. Kenny
    June 25, 2012 at 11:01 am

    I can’t run the demostration with NettoSphere, whatever url return 200 result

  5. YongGang
    October 29, 2012 at 8:13 am

    The code link seems broken, please check, thanks.

  6. November 21, 2012 at 2:43 am

    Hiya!

    The code is not working, and some of the links are dead. :(

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 51 other followers

%d bloggers like this: