Home > Atmosphere > Getting started with Atmosphere CPR part 1: Writing the HelloWord of Comet….a Chat application

Getting started with Atmosphere CPR part 1: Writing the HelloWord of Comet….a Chat application

Time to get started with Atmosphere CPR (Comet Portable Runtime)! In this first part, I will describe how to write a chat application and deploy in on Tomcat, Jetty and GlassFish.

Note: The Atmosphere Framework have evolved since the release of that blog. Please visit our web site for an updated sample and white paper


Let do the basic first. Let’s use Maven 2 and create the file structure:

 %  mvn archetype:create -DgroupId=org.atmosphere.samples
 -DartifactId=chat -DarchetypeArtifactId=maven-archetype-webapp

Which will create the following structure:


The next step is to add the required context.xml and save it under META-INF/


Finally, let’s add Atmosphere CPR library to the pom.xml so it gets added to our WEB-INF/lib


                <name>Java.net Repository for Maven 2</name>

We are now ready to write our first AtmosphereHandler, which is the central piece of any Atmosphere CPR application. Let’s just implement this interface

 38 package org.atmosphere.samples.chat;
 40 import java.io.IOException;
 41 import java.util.logging.Logger;
 42 import javax.servlet.http.HttpServletRequest;
 43 import javax.servlet.http.HttpServletResponse;
 44 import org.atmosphere.cpr.AtmosphereEvent;
 45 import org.atmosphere.cpr.AtmosphereHandler;
 46 import org.atmosphere.cpr.Broadcaster;
 48 /**
 49  * Simple AtmosphereHandler that implement the logic to build a Chat application.
 50  *
 53  */
 54 public class ChatAtmosphereHandler implements AtmosphereHandler {

As described here, implementing an AtmosphereHandler requires two methods:

     * When a client send a request to its associated {@link AtmosphereHandler}, it can decide
     * if the underlying connection can be suspended (creating a Continuation)
     * or handle the connection synchronously.
     * It is recommended to only suspend request for which HTTP method is a GET
     * and use the POST method to send data to the server, without marking the
     * connection as asynchronous.
     * @param event an {@link AtmosphereEvent}
     * @return the modified {@link AtmosphereEvent}
    public AtmosphereEvent onEvent(AtmosphereEvent event) throws IOException;

     * This method is invoked when the {@link Broadcaster} execute a broadcast
     * operations. When this method is invoked its associated {@link Broadcaster}, any
     * suspended connection will be allowed to write the data back to its
     * associated clients.
     * @param event an {@link AtmosphereEvent}
     * @return the modified {@link AtmosphereEvent}
    public AtmosphereEvent onMessage(AtmosphereEvent event) throws IOException;

The onEvent method will be invoked every time a request gets mapped to it associated AtmosphereHandler. There is two way to map request to an AtmosphereHandler. By default, the name of the AtmosphereHandler will be used, e.g. assuming we name our web application chat.war, a request to http://localhost:8080/chat/ChatAtmosphereHandler will invoke the AtmophereHandler.onEvent. For the chat, let’s assume we will suspend the response when the browser is sending us GET request

 77         HttpServletRequest req = event.getRequest();
 78         HttpServletResponse res = event.getResponse();
 80         res.setContentType("text/html");
 81         res.addHeader("Cache-Control", "private");
 82         res.addHeader("Pragma", "no-cache");
 83         if (req.getMethod().equalsIgnoreCase("GET")) {
 84             res.getWriter().write("<!-- Comet is a programming technique that enables web " +
 85                     "servers to send data to the client without having any need " +
 86                     "for the client to request it. -->\n");
 87             res.getWriter().flush();
 88             event.suspend();

The central piece is the AtmosphereEvent, from which we can retrieve the request and response object. Next we do some setup and then once we are ready we just need to invoke the AtmosphereEvent.suspend(), which will automatically tell Atmosphere CPR to not commit the response. Not committing the response means we can re-use it later for writing. In the current exercise, we will use the suspended response when someone enter join or enter sentence inside the chat room. Now let’s assume when a user logs in or enter sentences, the browser set a POST (posting some data). So when a user logs in

 90             res.setCharacterEncoding("UTF-8");
 91             String action = req.getParameterValues("action")[0];
 92             String name = req.getParameterValues("name")[0];
 94             if ("login".equals(action)) {
 95                 event.getBroadcaster().broadcast(
 96                         "System Message from "
 97                         + event.getWebServerName(), name + " has joined.");
 98                 res.getWriter().write("success");
 99                 res.getWriter().flush();

The important piece here is line 94. A Broadcaster’s role is to publish data to the suspended responses. As soon as you broadcast data, all suspended responses will be given a chance to write the content of the broadcast. Above we just broadcast the name and also which WebServer we are running on (for demo purpose). Calling Broadcaster.broadcast() will in turn invoke your AtmosphereHandler.onMessage will all suspended response. Here let’s assume we just reflect (write) what we receive:

124     public AtmosphereEvent
125             onMessage(AtmosphereEvent event) throws IOException {
126         HttpServletRequest req = event.getRequest();
127         HttpServletResponse res = event.getResponse();
128         res.getWriter().write(event.getMessage().toString());
129         res.getWriter().flush();
130         return event;
131     }

Next is when the user enter some chat messages:

100             } else if ("post".equals(action)) {
101                 String message = req.getParameterValues("message")[0];
102                 event.getBroadcaster().broadcast(BEGIN_SCRIPT_TAG + toJsonp(name, message) + END_SCRIPT_TAG);
103                 res.getWriter().write("success");
104                 res.getWriter().flush();

Here we encode the message using the JSON format so it make simple for the client’s javascript to update the page. That’s it for the AtmosphereHandler. You can see the complete source code here.

Now let’s assume we want to have a more fine grain way to map our AtmosphereHandler to the request. To achieve that, create a file called atmosphere.xml under src/main/webapp/META-INF/ and define the mapping you want:

  1 <atmosphere-handlers>
  2     <atmosphere-handler context-root="/chat" class-name="org.atmosphere.samples.chat.ChatAtmosphereHandler">
  3         <property name="name" value="Chat"/>
  4     </atmosphere-handler>
  5 </atmosphere-handlers>

With this file, all requests to /chat will be mapped to our ChatAtmosphereHandler.

Now let’s explore the client side. First, let’s write a very simple index.html file:

  1 <?xml version="1.0" encoding="UTF-8" ?>
  2 <!DOCTYPE html
  3 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  5 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  6     <head>
  7         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  8         <title>Atmosphere Chat</title>
  9         <link rel="stylesheet" href="stylesheets/default.css" type="text/css" />
 10         <script type="text/javascript" src="javascripts/prototype.js"></script>
 11         <script type="text/javascript" src="javascripts/behaviour.js"></script>
 12         <script type="text/javascript" src="javascripts/moo.fx.js"></script>
 13         <script type="text/javascript" src="javascripts/moo.fx.pack.js"></script>
 14         <script type="text/javascript" src="javascripts/application.js"></script>
 15     </head>
 16     <body>
 17         <div id="container">
 18             <div id="container-inner">
 19                 <div id="header">
 20                     <h1>Atmosphere Chat</h1>
 21                 </div>
 22                 <div id="main">
 23                     <div id="display">
 24                     </div>
 25                     <div id="form">
 26                         <div id="system-message">Please input your name:</div>
 27                         <div id="login-form">
 28                             <input id="login-name" type="text" />
 29                             <br />
 30                             <input id="login-button" type="button" value="Login" />
 31                         </div>
 32                         <div id="message-form" style="display: none;">
 33                             <div>
 34                                 <textarea id="message" name="message" rows="2" cols="40"></textarea>
 35                                 <br />
 36                                 <input id="post-button" type="button" value="Post Message" />
 37                             </div>
 38                         </div>
 39                     </div>
 40                 </div>
 41             </div>
 42         </div>
 43         <iframe id="comet-frame" style="display: none;"></iframe>
 44     </body>
 45 </html>

Simple form that will send back to the server the login’s name and the chat message entered. To update on the fly the interface as soon as our ChatAtmosphereHandler.onMessage write/send us data, let’s use prototype and behavior javascript. I’m assuming you are either familiar with those framework or have basic understanding how they work. This will be defined under application.js. As soon as the user enter its login name, let’s do

 12     login: function() {
 13         var name = $F('login-name');
 14         if(! name.length > 0) {
 15             $('system-message').style.color = 'red';
 16             $('login-name').focus();
 17             return;
 18         }
 19         $('system-message').style.color = '#2d2b3d';
 20         $('system-message').innerHTML = name + ':';
 22         $('login-button').disabled = true;
 23         $('login-form').style.display = 'none';
 24         $('message-form').style.display = '';
 26         var query =
 27         'action=login' +
 28         '&name=' + encodeURI($F('login-name'));
 29         new Ajax.Request(app.url, {
 30             postBody: query,
 31             onSuccess: function() {
 32                 $('message').focus();
 33             }
 34         });
 35     },

When the user write new chat message, let’s push

 36     post: function() {
 37         var message = $F('message');
 38         if(!message > 0) {
 39             return;
 40         }
 41         $('message').disabled = true;
 42         $('post-button').disabled = true;
 44         var query =
 45         'action=post' +
 46         '&name=' + encodeURI($F('login-name')) +
 47         '&message=' + encodeURI(message);
 48         new Ajax.Request(app.url, {
 49             postBody: query,
 50             requestHeaders: ['Content-Type',
 51             'application/x-www-form-urlencoded; charset=UTF-8'],
 52             onComplete: function() {
 53                 $('message').disabled = false;
 54                 $('post-button').disabled = false;
 55                 $('message').focus();
 56                 $('message').value = '';
 57             }
 58         });
 59     },

Now when we get response, we just update the page using

 60     update: function(data) {    
 61         var p = document.createElement('p');
 62         p.innerHTML = data.name + ':
' + data.message; 63 64 $('display').appendChild(p); 65 66 new Fx.Scroll('display').down(); 67 }

The way the index.html and application.js interact is simply defined by:

 69 var rules = {
 70     '#login-name': function(elem) {
 71         Event.observe(elem, 'keydown', function(e) {
 72             if(e.keyCode == 13) {
 73                 $('login-button').focus();
 74             }
 75         });
 76     },
 77     '#login-button': function(elem) {
 78         elem.onclick = app.login;
 79     },
 80     '#message': function(elem) {
 81         Event.observe(elem, 'keydown', function(e) {
 82             if(e.shiftKey && e.keyCode == 13) {
 83                 $('post-button').focus();
 84             }
 85         });
 86     },
 87     '#post-button': function(elem) {
 88         elem.onclick = app.post;
 89     }
 90 };
 91 Behaviour.addLoadEvent(app.initialize);
 92 Behaviour.register(rules);

See the complete source code here. So far so good, now we are ready to deploy our application into our favorite WebServer.

Here are simple pictures from different WebServer:

Glassfish v3








Wow that was easy! Download the war or src to get started. Follow us on Twitter for daily update about the project status and ask your questions using users@atmosphere.dev.java.net

var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.&#8221; : “http://www.&#8221;);
document.write(unescape(“%3Cscript src='” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));

var pageTracker = _gat._getTracker(“UA-3111670-3”);


Categories: Atmosphere
  1. Marian
    May 1, 2010 at 7:44 pm

    Does the example really work? I downloaded the war and put it in Jetty’s webapps folder – but the chat didn’t work:
    – new user is not visible
    – messages are not visibles

    I am using Java 6 & Jetty 6.1.24
    Any suggestions, please?

  2. May 3, 2010 at 2:17 pm

    The Sample you are trying are very old. Please download the latest 0.5.7 version from http://atmosphere.dev.java.net

  3. Marian
    May 3, 2010 at 8:30 pm

    OK, I’ve downloaded the war file from http://download.java.net/maven/2/org/atmosphere/samples/atmosphere-chat/0.5.7/ => no change! (i.e. does not work neither on Tomcat nor on Jetty).

    BTW the post speaks about a atmosphere.xml file, there’s no such file in the war!

  4. May 3, 2010 at 8:34 pm

    Strange, it works perfectly fine for me. Have you enabled The NIO connector on Tomcat? Let’s have the discussion on users@atmosphere.dev.java.net…can you post there your Tomcat log as well as the browser you are using? Thanks!!

  5. peter
    May 10, 2010 at 11:03 am

    For me the example is working fine in tomcat. But I had to remove the postfix number 5.7 – as you wrote in the readme.txt. However, I wounder that I do not find any specific Chat Servlet classes. Everything seems to be merged into the atmosphere-commons.jar and atmosphere-runtime.jar. Why is this examplespecific code in general atmosphere jar files???

  6. Jack Parker
    December 3, 2010 at 8:15 am

    Trying to understand the client side: is the ‘comet-iframe’ used?

    Is it possible to make comet work without the browser ‘spinner’?

  7. Jack Parker
    December 3, 2010 at 9:11 pm

    I see that AtmosphereHandler sends ‘app.listen’ (which in the .js uses comet-frame),
    but what is this all about? does every app need this ‘listen’/’comet-frame’?

    } else if (event.isResuming() || event.isResumedOnTimeout()) {
    String script = “window.parent.app.listen();\n”;


  8. Jfarcand
    December 6, 2010 at 1:17 pm

    Jack, take a look at a more recent demo of Atmosphere. This one was just an example of how you can use Javascript:


    Mainly you need to execute some JavaScript in order to make the client works. That’s why the application pass some window.parent… script.

    The atmosphere jquery plugin is much easier to use and take care of the spinning icon and use the best javascript depending on which browser you are using.

    — Jeanfrancois

  9. Siya
    December 7, 2010 at 2:26 pm

    Hi Jean

    I understand the broadcaster broadcasts to any suspended response in either the request,application or VM context.
    Now lets say I want to do chat rooms, which is the best way to separates the responses to write back to? Do I create a broadcaster per chat room?


  10. Vinodh
    April 25, 2011 at 1:05 pm

    Hi, I have downloaded war file “atmosphere-websocket-chat-0.7.1.war” from “http://atmosphere.dev.java.net” renamed to “atmosphere-websocket-chat.war” put in webapps Tomcat 7.0.12 started web server typed “http://localhost:8080/atmosphere-websocket-chat/” got 404 error so i gave “http://localhost:8080/atmosphere-websocket-chat/index.html” a html page opened but i give user name and click join but but nothing works I also tried other chat nothing works. Can you help me to run. Browser Safari 5.xx Windows o/s. Thanks

  11. August 8, 2011 at 2:03 pm

    Hi Jean-Francois ,

    I have been trying to install the atmosphere war file on my glassfish 3.1.1 server .
    I have enabled the js debugger with google so at first I had a 403 error but that’s because the application was looking for /atmosphere-chat/ when it was by default deployed to /atmosphere-chat-BETA…/

    now I don’t have a 403 error but I have a 500 internal error in the glassfish server and the following log from google debugger :

    Refused to set unsafe header “Connection”
    ChatAtmosphereHandlerPOST 500 (Internal Server Error)
    Refused to set unsafe header “Connection”
    ChatAtmosphereHandlerPOST 500 (Internal Server Error)

    ( the ip is my dedicated server ip )

    the java error on the server side is the following :

    [#|2011-08-08T13:53:03.482+0000|WARNING|glassfish3.1|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=247;_ThreadName=Thread-1;|StandardWrapperValve[Atmosphere Chat]: PWC1406: Servlet.service() for servlet Atmosphere Chat threw exception
    at org.atmosphere.samples.chat.ChatAtmosphereHandler.onEvent(ChatAtmosphereHandler.java:96)
    at org.atmosphere.cpr.WebContainer.action(WebContainer.java:115)
    at org.atmosphere.cpr.WebContainer.suspended(WebContainer.java:93)
    at org.atmosphere.container.JettyCometSupport.service(JettyCometSupport.java:89)
    at org.atmosphere.cpr.AtmosphereServlet.service(AtmosphereServlet.java:310)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:844)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1518)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:171)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:651)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:591)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:87)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:651)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:591)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:318)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:222)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:803)
    at com.sun.grizzly.comet.CometEngine.executeServlet(CometEngine.java:469)
    at com.sun.grizzly.comet.CometEngine.handle(CometEngine.java:338)
    at com.sun.grizzly.comet.CometAsyncFilter.doFilter(CometAsyncFilter.java:84)
    at com.sun.grizzly.arp.DefaultAsyncExecutor.invokeFilters(DefaultAsyncExecutor.java:164)
    at com.sun.grizzly.arp.DefaultAsyncExecutor.interrupt(DefaultAsyncExecutor.java:140)
    at com.sun.grizzly.arp.AsyncProcessorTask.doTask(AsyncProcessorTask.java:90)
    at com.sun.grizzly.http.TaskBase.run(TaskBase.java:190)
    at com.sun.grizzly.http.TaskBase.execute(TaskBase.java:172)
    at com.sun.grizzly.arp.DefaultAsyncHandler.handle(DefaultAsyncHandler.java:142)
    at com.sun.grizzly.arp.AsyncProtocolFilter.execute(AsyncProtocolFilter.java:174)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:526)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:507)
    at java.lang.Thread.run(Thread.java:662)

    • August 8, 2011 at 2:12 pm

      Which version of Atmosphere are you using? Try the latest 0.7.2. Thanks.

  12. August 8, 2011 at 2:13 pm

    I have been also using the latest war file from OSS , atmosphere-chat-0.6 but I does not waork either , it says

    GlassFish3.1 using javax.servlet/3.0:
    has suspended a connection from

    Delayed Chat message178.94.140.120:
    is still listening

    and the chrome console says Refused to set unsafe header “Connection”

    one other questions : all the links for the sources codes appears to be broken , and I cannot get the source on oss . Where could we find the source code of your tutorial ?

    with regards

  13. August 8, 2011 at 2:21 pm

    I also understood that atmosphere-Meteor was using the comet runtime of the application server where it was running so I assume I have not to install a specific atmosphere runtime since I already have the grizzly comet runtime running? Am i correct ?

  14. August 8, 2011 at 2:31 pm

    I’ve been now trying the atmosphere-meteor-chat-0.7.2.war and I got this time at the deployment :

    Exception while deploying the app : java.io.IOException: org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element ‘async-supported’. One of ‘{“http://java.sun.com/xml/ns/j2ee”:init-param, “http://java.sun.com/xml/ns/j2ee”:load-on-startup, “http://java.sun.com/xml/ns/j2ee”:run-as, “http://java.sun.com/xml/ns/j2ee”:security-role-ref}’ is expected.

    but I have no atmosphere component on the glasssfish modules directory so maybe that comes from that .

  15. August 9, 2011 at 12:02 pm

    apparently I must have to put atmosphere-runtime-0.7.2.jar in the modules of glassfish .
    and the atmosphere demo chat works. I had the incorrect feeling that atmosphere didn’t required any component to be installed on the server.

    I still have the message Refused to set unsafe header “Connection” from the chrome console but that does not seem to impact the functionality of the chat .

  16. August 9, 2011 at 12:16 pm

    when compiling the chat sources , I had ( additionally to link the relevant jar files to the project ) to put the following jar files in WEB-INF/lib : atmosphere-compat-jbossweb-0.7.2.jar

    but I was able to deploy the war file and the chat works …

    Compared to cometd/Bayeux it appears considerably simpler .

  17. August 10, 2011 at 9:15 am

    Now I have several problems with writing an applicationHandler. I just used the source code and published a web application to eclipse but it appears that the AtmosphereResource event is not sent by atmosphere runtime to the class where the processing is implemented ( e.g. in the onrequest function ) . I have used a debug of glassfish with eclipse and it is clear that when a request is done by the client , nothing goes to the onrequest(…) functions , no event is sent . I have read all the docs , wrote the atmosphere.xml file ( that was not present BTW in the src code from the atmosphere chat ) but this change nothing .

  18. August 10, 2011 at 11:19 am

    OK I have successfully refactored the code and it compiles OK and execution is OK .

    One of my main problems when refactoring is that I was not aware of the presence of the following entries in the web xml :


    Which is where you probably tells how atmosphere runtime where to redirect the events .Of course , that was why no events where reaching my class . Ok that closes the subject.

  19. Mukesh
    January 4, 2012 at 5:27 am

    I use above war in jboss and put it in deploy folder .
    When I run this application on web it give following error –

    javax.servlet.ServletException: Tomcat failed to detect this is a Comet application
    Please add the following content under your META-INF/context.xml of your war file.


    please suggest

    • January 11, 2012 at 4:08 pm


      have you enabled JBoss APR connector? The error message will disappear once the connection is enabled.


      – Jeanfrancois

  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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

%d bloggers like this: