Shing Wai Chan's Weblog

A Simple Comet Example: Hidden Frame and Long Polling

星期四 四月 03, 2008

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,

[6] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg
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 四月 22, 2008 at 11:31 上午 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 四月 23, 2008 at 06:43 下午 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 四月 25, 2008 at 05:56 下午 PDT #

[Trackback] Using Grizzly's Comet APIs (now available in Glassfish v3), I create a dead simple Comet example, with about 100 lines in two files.

Posted by Jim Driscoll's Blog on 五月 01, 2008 at 01:29 下午 PDT #

[Trackback] Using Grizzly's Comet APIs (now available in Glassfish v3), I create a dead simple Comet example, with about 100 lines in two files.

Posted by Jim Driscoll's Blog on 五月 04, 2008 at 02:13 下午 PDT #

jh

Posted by 203.99.215.11 on 五月 12, 2008 at 11:30 下午 PDT #

Post a Comment:
  • HTML Syntax: NOT allowed