A Simple Comet Example: Hidden Frame and Long Polling

Recently, there is a great interest in Comet technology. One can find many interesting articles in Comet Daily. Comet allows server and client to keep a live connection for communication. This provides a mechanism for server to update clients, instead of having classical polling. In this blog, I am going to share my experience about using Comet with hidden frame and long polling in GlassFish v3 Technology Preview 2 builds. I try to make example as simple as possible to illustrate the basic interactions there. If you want to learn more about Comet, then I recommend Jean-Francois' blogs.

Set up GlassFish v3

Download GlassFish v3 Technology Preview 2, unzip the file and start the server with jvm option v3.grizzlySupport=true to enable comet.

    java -Dv3.grizzlySupport=true -jar glassfish-10.0-SNAPSHOT.jar

We need the above jvm-option in today's build. This will not be needed when comet is enabled by default.

Comet Servlet Code

The comet servlet code is adapted from grizzly sample comet-counter, which uses Ajax client. The details of our serlvet is as follows:

  1. In init(ServletConfig), one registers a context path to CometEngine,

        ServletContext context = config.getServletContext();
        contextPath = context.getContextPath() + "/hidden_comet";
        CometEngine engine = CometEngine.getEngine();
        CometContext cometContext = engine.register(contextPath);
        cometContext.setExpirationDelay(30 \* 1000);

    where "/hidden_comet" is url-pattern of the comet servlet in web.xml. For testing purpose, one keeps the connection for 30 sec.

  2. In doGet(HttpServletRequest, HttpServletResponse), one looks up the CometContext and adds our CometHandler.

        CounterHandler handler = new CounterHandler();
        handler.attach(res);
        CometEngine engine = CometEngine.getEngine();
        CometContext context = engine.getCometContext(contextPath);
        context.addCometHandler(handler);

  3. In doPost(HttpServletRequest, HttpServletResponse), one increments the counter and then invokes the CometContext.notify, which will trigger the CometHandler.onEvent above.

        counter.incrementAndGet();
        CometEngine engine = CometEngine.getEngine();
        CometContext<?> context = engine.getCometContext(contextPath);
        notify(null);

    In addition, it forwards to count.html page for displaying the count.

        req.getRequestDispatcher("count.html").forward(req, res);

  4. Next, one need to have a class implementing CometHandler interface. Among methods in CometHandler, the most interesting one is onEvent(CometEvent).

        public void onEvent(CometEvent event) throws IOException {
            if (CometEvent.NOTIFY == event.getType()) {
                int count = counter.get();
                PrintWriter writer = response.getWriter();
                writer.write("<script type='text/javascript'>parent.counter.updateCount('" + count + "')</script>\\n");
                writer.flush();
                event.getCometContext().resumeCometHandler(this);
            }
        }

    In our case, it writes a Javascript back to client side. This will invoke the Javascript function updateCount in count.html. The onEvent also invokes resumeCometHandler. This is necessary as the polling connection will be dropped once it is used.

  5. To compile the above Java code, one needs to include javax.javaee\*.jar and grizzly-comet\*.jar in classpath.

Client Code

On client side, I will illustrate the technique of hidden frame. Basically, the main page will have at least two frames. One of them does the long polling and is hidden from user. In our case, the index.html consists two frames as follows:

    <iframe name="hidden" src="hidden_comet" frameborder="0" height="0" width="100%"><iframe>
    <iframe name="counter" src="count.html" frameborder="0" height="100%" width="100%"><iframe>

The first frame, which is hidden, is pointed to our Comet Servlet above through GET method. The second frame is to display the counter and submit button for incrementing the counter. The Javascript in count.html is very simple as follows:

    <script type='text/javascript'>
        function updateCount(c) {
            document.getElementById('count').innerHTML = c;
            parent.hidden.location.href = "hidden_comet";
         };
    </script>

How does it work?

One can download the sources and war file from here, and deploy the war file. Using two browsers to access http://localhost:8080/grizzly-comet-hidden/index.html and click on "Click" button on each browser separately. Then one sees that counts in both browsers will be updated whenever one clicks on one of them. The mechanism is outlined as follows:

  1. When the user accesses index.html, a browser will load two frames:
    • The "hidden" frame accesses our Comet Servlet through GET method. This allows the client to start long polling with server.
    • The "counter" frame loads a static count.html.
  2. When the user clicks on the button in count.html, it submits a POST request to our Comet Servlet. This triggers the CometHandler onEvent method and redirects back to count.html to display the count. The onEvent triggers the updateCount() JavaScript in "counter" frame, which will
    • update the count and
    • invoke the Comet Servlet doGet for long polling in "hidden" frame,

Note: There is a threading issue in the above sample code. I have posted the fix and other additional comments in another blog: Follow up on A Simple Comet Example: Long Polling vs Http Streaming.

Comments:

Hi Shing-Wai,

Great post!

You said that you have an Ajax client here, but I don't see you using the XmlHttpRequest. Isn't it just plain JavaScript you are using?

If it is really Ajax (but a form I'm not used to seeing), is it true that all Comet clients must be Ajax clients?

Thanks.
Jennifer

Posted by Jennifer on April 22, 2008 at 04:31 AM PDT #

Hi Jennifier,

This example has no Ajax in client.
Btw, I have a new blog which discuss Http Streaming:

http://blogs.sun.com/swchan/entry/follow_up_on_a_simple

Thanks.
Shing Wai

Posted by Shing Wai Chan on April 23, 2008 at 11:43 AM PDT #

If one download the latest gf v3 build, then one should add the following property in http-listener in domain.xml

<property name="cometSupport" value="true"/>

The jvm option, v3.grizzlySupport=true, mentioned above is no longer valid.

Posted by Shing Wai Chan on April 25, 2008 at 10:56 AM PDT #

Does Glassfish v2 (and sjsas 9.1) support Comet?
NetBeans plugin?

Posted by Niklas Norberg on May 26, 2008 at 11:57 PM PDT #

Shing Wai,
I'd like to use the DeliverResponse object in Bayeux, but need the clustering support of GlassFish v2ur2 (v3 Prelude does not support clustering yet). What would you recommend?
Thanks and Happy New,
Anthony

Posted by Anthony on December 30, 2008 at 06:30 AM PST #

With the recent change in domain.xml, can need to enable comet/cometd by adding an attribute enable-comet-support="true" on the http element under protocol. For instance, <network-config>/<protocols>/<protocol name="http-listener-1">/<http enable-comet-support="true" ...>

Posted by Shing Wai Chan on June 08, 2009 at 07:55 AM PDT #

Shing Wai,

Hi, I'm have a couple of days wondering why this example do work in IE but doesn't in Firefox. Some idea why?. Thanks

Posted by Mario on August 08, 2009 at 01:25 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

Shing Wai Chan

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today