Atmosphere is an asynchronous WebSocket/Comet framework for J2EE based Application Servers. In short, Atmosphere allows to push data to the web browser on near-real time fashion. For example, you could use such tool do develop a web based chat application or a stock market application that receives the new quotes as soon as they become available.
Unless WebSockets are used (a different protocol than HTTP), Atmosphere will work by having the server side intercepting the web call and keep the connection open. Like this, the client will be the one responsible for shutting the connection down and, by leaving this connection open, the server can now push data through that connection.
In addition, Atmosphere framework also provides:
1. A JQuery Plugin that, if used, highly simplifies the development of the client side.
2. A replacement for JAX-RS – not used in example because JBoss provides RESTEasy (note that RESTEasy can be substituted in favor of Atmosphere REST services if desired)
During my little experiments I’ve noticed that currently Atmosphere only allows the client to maintain one connection per page loaded.
Here is a presentation on Atmosphere:
The following example was successfully executed and tested JBoss AS 7.1 and you can download the example from github here.
The example works by having a web page loaded from the server. Once loaded the JQuery plugin will connect to the Atmosphere Servlet using a HTTP GET request that will override the default connection handler and keep the connection alive. To broadcast/push a message to a subscribed browser web connection simply do an HTTP POST to the desired subscription.
package crazypenguin.servlets; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.atmosphere.cpr.ApplicationConfig; import org.atmosphere.cpr.AtmosphereResource; import org.atmosphere.cpr.Broadcaster; import org.atmosphere.cpr.BroadcasterFactory; import org.atmosphere.cpr.HeaderConfig; import org.atmosphere.handler.AbstractReflectorAtmosphereHandler; import org.atmosphere.websocket.WebSocketEventListenerAdapter; public class PubSubAtmosphereHandler extends AbstractReflectorAtmosphereHandler { @Override public void onRequest(AtmosphereResource<HttpServletRequest, HttpServletResponse> event) throws IOException { HttpServletRequest req = event.getRequest(); HttpServletResponse res = event.getResponse(); String method = req.getMethod(); // Suspend the response. if ("GET".equalsIgnoreCase(method)) { // Log all events on the console, including WebSocket events. event.addEventListener(new WebSocketEventListenerAdapter()); res.setContentType("text/html;charset=ISO-8859-1"); Broadcaster b = lookupBroadcaster(req.getPathInfo()); event.setBroadcaster(b); String atmoTransport = req.getHeader(HeaderConfig.X_ATMOSPHERE_TRANSPORT); if (atmoTransport != null && !atmoTransport.isEmpty() && atmoTransport.equalsIgnoreCase(HeaderConfig.LONG_POLLING_TRANSPORT)) { req.setAttribute(ApplicationConfig.RESUME_ON_BROADCAST, Boolean.TRUE); event.suspend(-1, false); } else { event.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() { // empty } Broadcaster lookupBroadcaster(String pathInfo) { String[] decodedPath = pathInfo.split("/"); Broadcaster b = BroadcasterFactory.getDefault().lookup(decodedPath[decodedPath.length - 1], true); return b; } }
RESTEasy example call when called from the web page button will, within the server side broadcast a message to all subscribed connections.
package crazypenguin.rest; import org.atmosphere.cpr.Broadcaster; import org.atmosphere.cpr.BroadcasterFactory; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.Response; @Path("/control") @Stateless @LocalBean //Note: @javax.ejb.Local(WindowingResource.class) does not work on AS 7! public class AtmosphereTestBean implements AtmosphereTest { private boolean m_visible = false; @POST @Path("/toggle") @Override public Response toggle(String xml) { m_visible = !m_visible; notifySubscribers(m_visible ? "ON" : "OFF"); return Response.ok().build(); } private void notifySubscribers(String state) { Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup("control", true); if (broadcaster != null) broadcaster.broadcast(state); } }
<!DOCTYPE html> <html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <meta charset="UTF-8"> <title>Atmosphere Test</title> </head> <body> <input type="button" value="POST to server..." onclick="post()"/> <div class="controlPanel"> <div id="cp-1" class="cpitem"><img src="img/remoting.png"/></div> </div> <script src="js/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="js/jquery.atmosphere.js" type="text/javascript"></script> <script src="js/jquery.url.js" type="text/javascript"></script> <script src="js/atmo.js" type="text/javascript"></script> </body> </html>
Simple JavaScript object responsible for subscribing to server broadcasts and calling the registered message handler methods.
var cm; var cm_actions = { 'ON':['showItem', '#cp-1'], 'OFF':['hideItem', '#cp-1'], }; $(document).ready(function(){ $('#cp-1').hide(); cm = new ControlManager(); }); function controlConnect() { controlUnsubscribe(); controlSubscribe(); } // jquery.atmosphere.response function callback(response) { // Websocket events. $.atmosphere.log('info', ["response.state: " + response.state]); $.atmosphere.log('info', ["response.transport: " + response.transport]); $.atmosphere.log('info', ["response.status: " + response.status]); if (response.transport != 'polling' && response.state == 'messageReceived') { $.atmosphere.log('info', ["response.responseBody: " + response.responseBody]); if (response.status == 200) { var data = response.responseBody; if (data.length > 0) { for (var cmd in cm.actions) { if (data === cmd) { var m = window[cm.actions[cmd][0]]; var ma; if (cm.actions[cmd].length > 1) ma = cm.actions[cmd][1]; if (ma && ma!= '') m(ma); else m(); } } } } } } function controlSubscribe() { if (!this.callbackAdded) { var url = $.url(); var location = url.attr('protocol') + '://' + url.attr('host') + ':' + url.attr('port') + '/atmo/pubsub/control'; this.connectedEndpoint = $.atmosphere.subscribe(location, !callbackAdded ? this.callback : null, $.atmosphere.request = { transport: 'websocket' } ); this.callbackAdded = true; } } function controlUnsubscribe(){ this.callbackAdded = false; $.atmosphere.unsubscribe(); } function ControlManager() { this.suscribe = controlSubscribe; this.unsubscribe = controlUnsubscribe; this.connect = controlConnect; this.callback = callback; this.connect(); this.actions = cm_actions; } function showItem(id) { $(id).show(); } function hideItem(id) { $(id).hide(); } function post() { var request = $.ajax({ url: 'rest/control/toggle', type: 'POST', data: '<doc>foo</doc>', contentType:"application/xml; charset=utf-8", }); request.fail(function(jqXHR, textStatus) { alert( "Request failed: " + textStatus ); }); }
Atmosphere is a great library and provides many more features. Make sure you explore it deeply since it can really make your Web 2.0 applications much more interesting and responsive.
Thank you ! Been looking for this for my https://github.com/soluvas/primefaces-bootstrap project 🙂
Looks good! I’ll probably try it sometime soon.
Hi. I am getting an error when I open the index.html page in the firefox debugger console.
Firefox is not able to connect to websocket ws://lo….
version of firefox : 14
Application server : JBoss As 7
and probably because of this , when I click on the POST to Server button the application fails at the line
private void notifySubscribers(String state) {
// fails at BroadcasterFactory.getDefault()
Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(“control”, true);
if (broadcaster != null) broadcaster.broadcast(state);
}
would appreciate if there is any help around.
JBoss AS 7 uses JBossWeb for the servlet container and, as far as I know, it does not provide WebSockets as of yet.
That’s why Atmosphere is a great solution because in the day it does, your app will be ready and automatically detect it.
Atmosphere detects if it can make use of web sockets and will automatically downgrade to a supported messaging format (e.g. long-polling).
Thanks. But Why do I get a Null pointer exception in the following line when I click Post to server button
private void notifySubscribers(String state) {
// NullPointerException fails at BroadcasterFactory.getDefault()
Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(“control”, true);
if (broadcaster != null) broadcaster.broadcast(state);
}
mel,
Most probably because you’re missing some dependencies and the Broadcaster fails to load and return the default instance.
Post some of the exception stack trace from your server log. Also, make sure you have the necessary libs available in your project.
I just tested the provided sample EAR and everything is working just fine with JBoss 7.1.1 and Firefox 14.
Thanks again for your valuable time.
would you be in a position to attach your project, please…
would be really helpful.
Thanks
Mel,
You can checkout the maven project from github at:
https://github.com/crazyfrozenpenguin/cpenguin_repository/tree/master/jboss7
Let me know if it works.
ello,
I did the steps exactly as you specified and deployed it on JBoss Brontes
and when I click on the Post to Server button, The browser shows a alert message Request failed and below id the stacktrace on the console
Hello, I deployed the version from github and I was able to have a clean deployment.
Now when I click on the Post to Server using firefox there is nothing new on the screen but the firefox web console displaying
[14:03:50.941] POST http://localhost:8080/atmo-web/rest/control/toggle [HTTP/1.1 200 OK 0ms]
no element found
what could be the issue ?
regards Mel
Date: Mon, 20 Aug 2012 06:21:03 +0000 To: pereiramelvin@hotmail.com
Mel,
Forget the steps in the README.rd file. That was auto generated by the JBoss archtype and I really didn’t read through.
Basically, all you have to do is to get the atmo directory to your local disk and in the command line use maven 3 to build it:
mvn clean install
You should get a atmo.ear file under atmo/atmo-ear/target directory.
Copy that file into your JBOSS_HOME/standalone/deployments
In this same directory create an empty text file names:
atmo.ear.dodeploy
Start jboss in standalone mode: JBOSS_HOME/bin/standalone.sh
Everything should start just fine but keep an eye in your console.
Point your browser to:
http://localhost:8080/atmo-web/index.html
and it should work.
I’ve just tried this process locally and it build just fine and worked without any issues in Chrome and Firefox (latest versions).
Hi,
Thanks again for your help and patience.
I did a clean build and I did deploy it perfectly on JBoss Brontes
Now when I click on the Post to Server message, there are no stack trace logs, but
nothing happens on the browser screen.
and I get a message on the firefox web console :
no element found
I am still not sure abt the reason.
What should it show on the browser screen when you click on Post to server.
again thanks for your help and patience.
I really need this example working.
also for your reference I would like you to point you to something thats happening on JBoss 7.1.2
https://github.com/mikebrock/jboss-websockets
Date: Mon, 20 Aug 2012 13:18:16 +0000 To: pereiramelvin@hotmail.com
yep. I got it. infact it was running successfully since morning but for one reason.
I have gzip compression on in JBoss Brontes.
If I switch on compression the app is not able to display the toggle image.
But if I switch the compression off I am able to toggle the image.
Infact I have created a lot of rest services utilizing jquery, JAX-RS and compression on.
Why this one should fail is a mystery.
Thanks a lot for so much of efforts on your part.
warm regards Mel
Date: Mon, 20 Aug 2012 13:18:16 +0000 To: pereiramelvin@hotmail.com
Theoretically, it should work even with compression on. But then again I certainly did not try it.
Either way, I glad that you got it working. Hope it’s of good use to you.
Yep. Thanks a lot.
By the way , were you able to run any of the samples from theatmosphere distro on JBoss Brontes.
I tried a few, but was not able to mostly because of the dependence on grizzly.
thanks again
warm regards Mel
Date: Mon, 20 Aug 2012 16:23:32 +0000 To: pereiramelvin@hotmail.com
Melvin,
I have not tried it yet. Once I get some free time I’ll give it a go.
In addition, please notice that there were some minor changes to the code due to an upgrade to the latest stable Atmosphere version: 0.9.7
Thanks a lot again for your patience and valuable time.
I ‘ll check this out and notify you of the results.
Have a nice time.
warm regards Mel
Date: Mon, 20 Aug 2012 06:24:46 +0000 To: pereiramelvin@hotmail.com