The Source for Java Technology Collaboration
User: Password:



Jean-Francois Arcand

Jean-Francois Arcand's Blog

Writing a Comet web application using GlassFish

Posted by jfarcand on October 31, 2006 at 07:09 PM | Comments (18)

This blog describes how to write Comet enabled web application using GlassFish's Comet Engine.

A couple of months ago, I've blogged about the technical details of the GlassFish's Comet support. Since then, I've got a lot of feedbacks on the blog and also privately. Surprisingly, a lot of peoples have started using the API and an asked for a blog describing a basic example. So here it comes ... a basic Chat Servlet :-)

werixc.jpg

First, to enable Comet Support in GlassFish, add the following in ${glassfish.home}/domains/domain1/config/domain.xml


        <http-listener acceptor-threads="1" address="0.0.0.0" 
           blocking-enabled="false" default-virtual-server="server"
           enabled="true" family="inet" id="http-listener-1" port="8080"
           security-enabled="false" server-name="" xpowered-by="true">
                <property name="cometSupport" value="true"/>
        </http-listener>

Next, add in you web.xml:


        <load-on-startup>0</load-on-startup>

OK now the interesting parts. The first things to decide when writing a Comet enabled web app is the component that will get polled. For this example, I will use a Servlet. First, the Servlet needs to register to the CometEngine:


   48     public void init(ServletConfig config) throws ServletException {
   49         super.init(config);
   50         contextPath = config.getServletContext().getContextPath() + "/chat";
   51         CometEngine cometEngine = CometEngine.getEngine();
   52         CometContext context = cometEngine.register(contextPath);
   53         context.setExpirationDelay(60 * 1000);
   54     }

The important part to define first is the context path that will be considered for Comet processing (or polling). All requests that takes the form of http://:/context/chat will be considered for polling. The context.setExpirationDelay() will determine how long a request will be polled. For this example, I've set the expiration delay to 60 seconds. After 60 seconds, the polled connection will be closed.

Next, you need to define a Comet request Handler which will get invoked every time the CometContext is updated. For the Chat, the handler will be created after the user has entered its user name (by issuing http://.../login.jsp)


   71                 if ("login".equals(action)) {
   72                     String username = request.getParameter("username");
   73                     request.getSession(true).setAttribute("username", username);
   74
   75                     if (firstServlet != -1){
   76                          cometContext.notify("User " + username
   77                           + " from " + request.getRemoteAddr()
   78                           + " is joinning the chat.",CometEvent.NOTIFY,
   79                                  firstServlet);
   80                     }
   81
   82                     response.sendRedirect("chat.jsp");
   83                     return;
   84                 } else if ("post".equals(action)){
   85                     String username = (String) request.getSession(true)
   86                         .getAttribute("username");
   87                     String message = request.getParameter("message");
   88                     cometContext.notify("[ " + username + " ]  "
   89                             + message + "<br/>");
   90                     response.sendRedirect("post.jsp");
   91                     return;
   92                 } else if ("openchat".equals(action)) {
   93                     response.setContentType("text/html");
   94                     CometRequestHandler handler = new CometRequestHandler();
   95                     handler.clientIP = request.getRemoteAddr();
   96                     handler.attach(response.getWriter());
   97                     cometContext.addCometHandler(handler);
   98                     String username = (String) request.getSession(true)
   99                         .getAttribute("username");
  100                     response.getWriter().println("<h2>Welcome "
  101                             + username + " </h2>");
  102                     return;

After the user has logged in, the browser will be redirected to the chat.jsp page, which will sent the action="openchat". The CometHandler (the class that will update the chat message box) implementation looks like:


  134         public void onEvent(CometEvent event) throws IOException{
  135             try{
  136
  137                 if (firstServlet != -1 && this.hashCode() != firstServlet){
  138                      event.getCometContext().notify("User " + clientIP
  139                       + " is getting a new message.",CometEvent.NOTIFY,
  140                              firstServlet);
  141                 }
  142                 if ( event.getType() != CometEvent.READ ){
  143                     printWriter.println(event.attachment());
  144                     printWriter.flush();
  145                 }
  146             } catch (Throwable t){
  147                t.printStackTrace();
  148             }
  149         }
  150
  151
  152         public void onInitialize(CometEvent event) throws IOException{
                      ....
  156         }

Every time the user will post a new message, the CometHandler.onEvent(...) will be invoked and the Chat message pushed back to the browser.

On the client side, the chat.jsp page looks like


     26 <frameset>
     27   <iframe name="chat" src ="/comet/chat?action=openchat" width="100%" scrolling="auto"></iframe>
     28   <iframe name="post" src="post.jsp" width="100%" scrolling="no"/>
     29 </frameset>

You can download the application (which include the src) here.

Note that the application described here is really to give an example. I would never recommend the use of static variables like I did in the example.

werixc96.jpg

Before I forgot, one interesting feature I've recently added (was requested on first blog on Grizzly's Comet) is the ability to update a single CometHandler (or a single polled request). When calling cometContext.addCometHandler(..), the returned value can be later re-used to push datas only to that cometHandler by doing:


     cometContext.notify(String message, int type, String cometListenerID);

See the API for more info. For the Chat example, I've added a pop up window where a chat moderator receives all the chat messages, who is connected and from where:


    138   event.getCometContext().notify("User " + clientIP
    139      + " is getting a new message.",CometEvent.NOTIFY,
    140        firstServlet);

That's it. Very simple, is it? No needs to spawn a thread anywhere on the Servlet side, no special Servlet operations, etc.

Once I've a chance, I will try to use AJAX and improve the client. Any help is appreciated on that side :-) As usual, thanks for all the feedbacks sent by emails!

technorati:


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • I write similar explanations in spanish on my blog http://weblogs.javahispano.org/lasterra/entry/mi_primera_aplicacion_comet_en Did you try the example in glassfish v2 beta? I think there is some problems with proxiedProtocols property on the listener. Regards.

    Posted by: lasterra on June 07, 2007 at 03:46 AM

  • Thanks Enrique. I've fixed the NPE, the fix will be available in b51. The workaround now is to remove the . Good work with your blog!

    Posted by: jfarcand on June 07, 2007 at 10:33 AM

  • I run the example in glassfish v2-b41d, when I logged in I got a "Chat closed" message, application seem stopped. why it does not work? patrick

    Posted by: mk926 on June 28, 2007 at 10:55 PM

  • Hi, edit glassfish.home/domains/domain1/config/domain.xml and remove line <property ="proxiedProtocols" value=:ws/tcp"/> as b41d seems to be broken. If you can, use the latest build (b52) to use the latest Comet implementation. Thanks!

    Posted by: jfarcand on June 29, 2007 at 07:15 AM

  • hi, actually I do remove the property in domain.xml when i run it, otherwise the application cannot start up even. I run the example in v2-b53, it is cannot run neither. thanks, patrick

    Posted by: mk926 on July 01, 2007 at 07:13 PM

  • Hi jfarcand, I encounter the same problem with mk926 while using sjsas9.1. How can I remedy this problem. Thanks Jimmy.

    Posted by: chankokchern on July 04, 2007 at 07:06 PM

  • Just to make sure I understand, you have enabled Comet in domain.xml by adding the above property, right? Thanks

    Posted by: jfarcand on July 05, 2007 at 07:59 AM

  • Thanks jfarcand. After sometime meddling around, i think i make it works. Thanks for the help

    Posted by: chankokchern on July 05, 2007 at 05:47 PM

  • I do add the "" to domain.xml. thanks, patrick

    Posted by: mk926 on July 10, 2007 at 05:52 PM

  • I do add the to domain.xml. thanks, patrick

    Posted by: mk926 on July 10, 2007 at 05:53 PM

  • I do add the property name="cometSupport" value="true" to domain.xml

    Posted by: mk926 on July 10, 2007 at 05:55 PM

  • This is really strange. We have a lot of Comet applications already running and none of them are reporting this problem. Which GlassFish version are you using? Any error in domain.xml? Just post your question on users@glassfish.dev.java.net for fastest response! Thanks.

    Posted by: jfarcand on July 12, 2007 at 07:14 AM

  • When you say that "When calling cometContext.addCometHandler(..), the returned value can be later re-used to push datas only to that cometHandler" is this value the handler's hashCode() ? but hashCode() is not a unique identifier, so why not allowing the author of the handler to use whatever id he likes? Or I missed something? Nicolas

    Posted by: npi on October 22, 2007 at 06:52 AM

  • I agree the API can be improved to avoid using the hashCode. Internally I needed a way to locate the Os file descriptor of the CometHandler and the hashCode was the most trivial way. Note that this only apply to the case when the you need to notify a single CometHandler. Patches are welcome :-) Thanks!!

    Posted by: jfarcand on October 22, 2007 at 07:12 AM

  • Hi Jean François, I try to implement comet on a web application using Tomcat 5.5. And I search how to register Comet to Tomcat which is done with the domain.xml file with Glassfich. Have you any ideas? Am i forced to change to Tomcat 6.0 to use Comet? Thanks in advance for your response.

    Posted by: mkrupa on December 12, 2007 at 09:27 AM

  • Hi, Tomcat doesn't implement (yet :-)) Grizzly Comet. For Tomcat, take a look at: http://tomcat.apache.org/tomcat-6.0-doc/aio.html -- Jeanfrancois

    Posted by: jfarcand on December 12, 2007 at 09:34 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds