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!


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
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
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.
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?
I can’t run the demostration with NettoSphere, whatever url return 200 result
The code link seems broken, please check, thanks.
Hiya!
The code is not working, and some of the links are dead.