Getting Started with Socket.IO - Atmosphere/atmosphere GitHub Wiki

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 work 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 applications 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 work with Socket.IO WITHOUT ANY CHANGE! That means you can easily throw away your atmosphere.js code and 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 = "org.atmosphere.interceptor.
   AtmosphereResourceLifecycleInterceptor")
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(...);
        }
    }

    @Override
    public void destroy() {
    }
}

Let's make it clear, I could have used a JSON parser but for the sake of this example 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 are 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', message);

    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 does not 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 
            + '"&gt;' + author + '&lt;/span&gt; @ ' +
            + (datetime.getHours() &lt; 10 ? '0' 
            + datetime.getHours() : datetime.getHours()) + ':'
            + (datetime.getMinutes() &lt; 10 ? '0' 
            + datetime.getMinutes() : datetime.getMinutes())
            + ': ' + message + '&lt;/p&gt;');
    }
});

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, isn't it? Now, depending on the browser you are using, Websockets or Long-polling will be used, TRANSPARENTLY! You can browse the complete code here.

⚠️ **GitHub.com Fallback** ⚠️