The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


The Grizzly Comet or why space shuttle Discovery launch was delayed.

Posted by jfarcand on July 6, 2006 at 12:12 PM PDT

Space shuttle Discovery was delayed recently, and the real reason was kept secret. Something strange was observed by the Hubble Space Telescope. The Hubble Ultra Deep Field(HUDF) image was showing a new star coming extremely fast to earth. Even after washing the main mirror with AJAX, the HUDF was clear: the Grizzly Comet is entering our atmosphere....

comet.jpg

This time I will discuss the new Comet support in Grizzly (sometimes called request polling, http streaming or continuation), build on top of Grizzly's Asynchronous Request Processing(ARP). From Wikipedia:

Comet is a programming technique that enables web servers
to send data to the client without having any need for the client
to request for it. It allows creation of event-driven web 
applications which are hosted in the browser.

After I've blogged about ARP, I've started defining some Comet APIs on top of it. I was waiting for free time to define the perfect API. I think I was dreaming....I will never have free time with Grizzly! Since Comet Request processing is more and more popular (Jetty supports it for a while, Tomcat just have a fresh implementation...and a new NIO based connector (finally!!!), and GlassFish have ARP), I've decided to make available my own implementation. I didn't update my implementation based on Greg Wilkins' proposal, but hey, this is build on top of ARP and once the Servlet EG approves Comet support, it will be easy to implement it.

The next couple of paragraphs will introduce the API. I did the famous Chat implementation using jMaki, and will soon publish it once Greg reviewed my modifications to his jMaki application :-)

Comet API

A component (Servlet, JSP, JSF or a java class) that wants to support Comet requests first need to register to the CometEngine:


        CometEngine cometEngine = CometEngine.getEngine();
        CometContext context = cometEngine.register(contextPath);    

Mainly, you first get an instance of CometEngine, then register the context path on which Comet requests will be allowed. The CometContext is the main object a component will use to interact with others Comet requests, implemented as CometHandler.


         // Class that implement CometHandler interface
         CometResponseHandler handler = new CometResponseHandler();
         handler.attach(httpServletResponse);
         CometContext cometContext = 
            cometEngine.getCometContext(contextPath);
         cometContext.addCometHandler(handler);
         cometContext.notify("User X just entered the room");

The CometHandler interface is quite simple:


    /**
     * Attach an instance of E to this class.
     */
    public void attach(E attachment);
    
    /**
     * Receive CometEvent notification.
     */
    public void onEvent(CometEvent event) throws IOException;   
    
    /**
     * Receive CometEvent notification when the underlying 
     * tcp communication is started by the client
     */
    public void onInitialize(CometEvent event) throws IOException;   
    
    /**
     * Receive CometEvent notification when the underlying 
     * tcp communication is closed by the CometHandler
     */
    public void onTerminate(CometEvent event) throws IOException;    
      
    /**
     * Receive CometEvent notification when the underlying 
     * tcp communication is interrupted by the Grizzly ARP.
     */
    public void onInterrupt(CometEvent event) throws IOException;

Below is an example of a CometHandler implementation


    public class CometResponseHandler implements CometHandler{
        
        private HttpServletResponse httpServletResponse;
        
        public void attach(HttpServletResponse httpServletResponse){
            this.httpServletResponse = httpServletResponse;
        }
                
        public void onEvent(CometEvent event) throws IOException{   
            System.out.println("==== onEvent =====");
            try{
                PrintWriter printWriter = httpServletResponse.getWriter();
                // We just received a new chat message from another user.
                // Flush it to the browser.
                printWriter.println(event.attachment());
                printWriter.flush();
            } catch (Throwable t){
               t.printStackTrace(); 
            }  
        }

        public void onInitialize(CometEvent event) throws IOException{  
        }

        public void onTerminate(CometEvent event) throws IOException{
            onInterrupt(event);
        }

        public void onInterrupt(CometEvent event) throws IOException{
            CometContext cometContext = event.getCometContext();  
            cometContext.removeCometHandler(this);
        }        
    }

Once added to a CometContext, the CometHandler will be invoked everytime the CometContext.notify(Object) is invoked (ex: A new message is added to a Chat forum):


        cometContext.notify(new Message
          ("Grizzly Comet", userId + " has entered our atmosphere."));

The CometHandler can be invoked by the Grizzly ARP via the CometEngine and by others CometHandler. The CometEngine will notify CometHandler when the request is received (onInitialize) and when the request is interrupted (onInterrupt). The polled request will be resumed when the delay set on the CometContext expires:


         context.setExpirationDelay(60 * 1000); //60 seconds

Using the Chat example, a Servlet will use the CometContext to notify the CometHandler when a new chat user is added or when a new message is received:

    public void doPost(HttpServletRequest request, 
                       HttpServletResponse  response)
            throws IOException, ServletException {                      
         try {
            String action = request.getParameter("action");
            // negotiate a userid
            if ("valid-register".equals(action)) {
                ....
            } else if ("register".equals(action)) {
                ....
                CometEngine cometEngine = CometEngine.getEngine();
                CometContext cometContext = 
                        cometEngine.getCometContext(contextPath);
                cometContext.notify(
                   new Message("Chatter", userId + " has joined."));               
            } else if ("add-message".equals(action)) {
                .....
                CometEngine cometEngine = CometEngine.getEngine();
                CometContext cometContext = 
                        cometEngine.getCometContext(contextPath);
                cometContext.notify(new Message(userId,msg);
            }
            ....

From an AJAX application, an http connection will be openned and a CometHandler will be created using the HttpServletResponse object created for that connection. The CometHandler.onEvent(..) will wait for CometEvent, and based on the CometEvent.attachment(), will proceed by pushing back data to the AJAX application. The CometEvent.attachment() can be any type. For a Chat application, you will most probably attach a String containing the new message.

Technical details

In Grizzly ARP, the Comet request isn't holding a Thread, but instead polled using NIO Selector (unfortunately using the SelectionKey.attach(..) for now ;-)). For scalability, it is very important to avoid holding a Thread during the request polling. Another important detail is the CometHandler doesn't need to manage any Threads when notifying others CometHandler. This is implemented by the Grizzly ARP directly and not exposed to the CometHandler API. Of cource CometHandler can do whatever they want, hence they can decide to implement different strategies using Threads to notify others CometHandler.

The time where the polling happens can be configured. In the chat example, the polling will occurs after the Servlet.service() has been invoked. In the GMail/JavaMail example, the polling happens before Servlet.service(). You can decide when polling will happens using the CometEngine.register(..) method:


        CometEngine cometEngine = CometEngine.getEngine();
        CometContext context = 
           cometEngine.register(contextPath, 
                                CometEngine.BEFORE_REQUEST_PROCESSING);    

see CometEngine implementation for more details.

When notifying CometHandler, you can specify the type of CometEvent you want to push:


         CometEngine cometEngine = CometEngine.getEngine();
         CometContext cometContext = 
             cometEngine.getCometContext(contextPath);
         cometContext.notify("Closing the chat room",
                             CometEvent.TERMINATE);

see CometEvent for more details.

I'm still thinking about how the CometContext is retrieved from a component:


         cometContext = cometEngine.getCometContext(contextPath);

A Servlet from another application can easily compute the contextPath and retrieve the "external" CometContext. Although I can envision very interesting applications who are sharing CometContext and CometHandler, a security mechanism needs to be added to allow configuring the shareability of CometContext.

Finally, Grizzly ARP works with clean and SSL connection (both supported by NIO non blocking socket), thus Comet support can be used for secure and non secure communication.

comet-2.jpg

La suite des choses

The Comet support is fairly new and available only starting in GlassFish 9.1 ea-b10. That means the implementation most probably need improvements. As usual, any feedback is welcomed. I will soon add to the Grizzly workspace a couple examples on how to use the API with AJAX client. More important, if you don't like the current implementation, it is very easy to extend Grizzly ARP with a completely different Comet support approach. I saw some implementation at JavaOne :-)

P.S To enable Comet Support, just add, in GlassFish 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>

and make sure, in case you are using a Servlet|JSP, that it is initialized when GlassFish starts up by adding in your web.xml:


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

UPDATED: A second blog that describe an example can be read here.

technorati:

Related Topics >> Java Enterprise      
Comments
Comments are listed in date ascending order (oldest first)

Jeanfrancois- Your help is greatly appreciated. Sorry if I keep asking many questions, I am just starting to get a hang of this new and powerful feature. >>>When are you setting the timeout? This is how I set the expiration delay for the CometContext: in the init() of a Servlet A engine = CometEngine.getEngine(); metaContext = engine.register(ServletB.CONTEXT_PATH); metaContext.setExpirationDelay(24*60*60*1000); Then in the doPost() of a Servlet B: engine=CometEngine.getEngine(); metaContext = engine.getCometContext(ServletB.CONTEXT_PATH); metaContext.setExpirationDelay(24*60*60*1000); handler = new CometResponseHandler(...); metaContext.addCometHandler(handler) (notice, ServletB.CONTEXT_PATH above is indeed servlet B's true context path) In this case, the handler's onInterrupt() will get called 30 seconds later IF I don't do any notify() on the CometContext. (IF I do call notify(), I notice the setExpirationDalay() seems to work--->the handler's onInterrupt() will be called 24 hrs later...) >>>Just a quick question: how will you find the clients you are looking at? For ways to "identify" which handler to notify, can we have an ID (a String type) for every handler, so later on we can notify only one handler if we want?

I'm also seeing some exception in the server logs. Take this example, I notify handlers inside Servlet B, like so: engine = CometEngine.getEngine(); metaContext = engine.getCometContext(ServletB.CONTEXT_PATH); if (rquest.getParameter("mode")!=null) metaContext.notify("notify handlers.."); However, in my log file, I see exceptions: [#|2006-10-23T16:13:11.834-0700|SEVERE|sun-appserver-pe9.1|javax.enterprise.system.container.web|_ThreadID=14;_ThreadName=CometSelector;_RequestID=2fabe282-4a28-43f2-82e3-f1d8a8264b0e;|CometSelector java.nio.channels.ClosedChannelException at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:167) at java.nio.channels.SelectableChannel.register(SelectableChannel.java:254) at com.sun.enterprise.web.connector.grizzly.comet.CometSelector$1.run(CometSelector.java:137) |#] and also: [#|2006-10-23T10:35:57.062-0700|SEVERE|sun-appserver-pe9.1|javax.enterprise.system.container.web|_ThreadID=16;_ThreadName=CometSelector;_RequestID=e2d032cf-5991-4e1a-b68f-dc771e8873a3;|CometSelector java.lang.IllegalStateException at com.sun.enterprise.web.connector.grizzly.async.DefaultAsyncHandler.handle(DefaultAsyncHandler.java:159) at com.sun.enterprise.web.connector.grizzly.comet.CometAsyncFilter.resume(CometAsyncFilter.java:140) at com.sun.enterprise.web.connector.grizzly.comet.CometEngine.interrupt(CometEngine.java:333) at com.sun.enterprise.web.connector.grizzly.comet.CometSelector.cancelKey(CometSelector.java:192) at com.sun.enterprise.web.connector.grizzly.comet.CometSelector.expireIdleKeys(CometSelector.java:179) at com.sun.enterprise.web.connector.grizzly.comet.CometSelector$1.run(CometSelector.java:142) |#] I have a feeling I may not be doing something to avoid these exceptions. Could you share some of your thoughts on this? Is there a "standard" way to use the comet features? Like register CometContext in one servlet, addCometHandler in another servlet, and call CometContext.notify() in the 3rd one? In my handler's onEvent() or onInterrupt(), do I need to do something special like close or NOT to close the httpResponse's printWriter()? This is my current implementation for onEvent() and onInterrupt(), public void onEvent(CometEvent event) throws IOException{ DataOutputStream os = new DataOutputStream (httpServletResponse.getOutputStream() ); httpServletResponse.setContentLength(event.attachment().toString().getBytes().length); os.writeBytes(event.attachment().toString()); os.flush(); os.close(); onInterrupt(event); } public void onInterrupt(CometEvent event) throws IOException{ CometEngine cometEngine = CometEngine.getEngine(); CometContext cometContext = cometEngine.getCometContext(contextPath); cometContext.removeCometHandler(this); }

Josh, thanks for the clear use case!!! I need to publish an example and blog in more details because it seems I don't cover all the use case in my testing :-) First, about the java.lang.IllegalStateException. To avoid getting it during the first request, add the following in your web.xml: <load-on-startup>0</load-on-startup> For the expiration delay, you are right It didn't get taken into account. I've fixed the problem so you can try tomorrow's build I will also change the API so you can get an ID per handler. ThanksJeanfrancois

Josh, for you second problem, the java.nio.channels.ClosedChannelException exception means one client has been closed during the notification. I wasn't able to reproduce the use case but looking at the code there was a possible thread race. The fix will also be available tomorrow. As for the os.close(), you shoud'nt call that method and let the CometEngine do it for you. I suspect that can cause problem, which means I need to prevent that case (or block) As soon as GlassFish m3 is completed I will blog and post an example that will show what I'm doing for testingThanksJeanfrancois

I've added a new CometContext.notify(Object,CometEvent.type,CometHandlerID) that allow pushing data to a subset of the registered CometHandlers. You can get the cometHandlerID when doing: int cometHandlerId = cometContext.addCometHandler(myHandler); Thanks for the feedback!!!!!

CometContext cometContext = cometEngine.getCometContext(contextPath);
cometContext.addCometHandler(handler); How can you call the addCometHandler method on a CometContext object ?
This one is described as protected in the API source code.

Last minute typo. I've fixed it in CVS so the next nightly (23/08) will contains the fix. Thanks!

I've wrote a test App using the Comet support on GlassFish. It seems that for some reason everytime I notify the CometContext with an event the CometHandler is calling onEvent 5 times delivering the same event on the 5 calls. Am I doing something wrong?

How do you register your Handler? Make sure the Handler is registered only one. Just send me your code if you want me to take a look :-)

It is a VERY VERY simple test. Just to the mechanics working Here it is: A servlet: import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sun.enterprise.web.connector.grizzly.comet.CometContext; import com.sun.enterprise.web.connector.grizzly.comet.CometEngine; import com.sun.enterprise.web.connector.grizzly.comet.CometEvent; import com.sun.enterprise.web.connector.grizzly.comet.CometHandler; public class CometChat extends HttpServlet { CometEngine engine = null; CometContext metaContext = null; @Override public void init() throws ServletException { super.init(); engine = CometEngine.getEngine(); metaContext = engine.register("/cometchat/meta"); Thread t = new Thread(){ public int i = 0; public void run(){ while(true){ try{ Thread.sleep(10000); metaContext.notify(new Integer(i++)); }catch (IOException e) { e.printStackTrace(); }catch (InterruptedException e) { e.printStackTrace(); return; } } } }; t.start(); } @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { CometResponseHandler handler = null; response.setContentType("text/plain"); response.setStatus(HttpServletResponse.SC_OK); if(request.getRequestURI().equals("/cometchat/meta")){ handler = new CometResponseHandler("/cometchat/meta"); handler.attach(response); metaContext.addCometHandler(handler); } PrintWriter writer = response.getWriter(); writer.flush(); writer.println("---START---"); writer.flush(); } /** * @param args */ public static void main(String[] args) { } } and a CometHandler import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.http.HttpServletResponse; import com.sun.enterprise.web.connector.grizzly.comet.CometContext; import com.sun.enterprise.web.connector.grizzly.comet.CometEngine; import com.sun.enterprise.web.connector.grizzly.comet.CometEvent; import com.sun.enterprise.web.connector.grizzly.comet.CometHandler; public class CometResponseHandler implements CometHandler { private HttpServletResponse httpServletResponse; private String contextPath = null; public CometResponseHandler(String contextPath){ } public void attach(HttpServletResponse httpServletResponse){ this.httpServletResponse = httpServletResponse; } public void onEvent(CometEvent event) throws IOException{ System.out.println("==== onEvent ====="); PrintWriter printWriter = httpServletResponse.getWriter(); printWriter.println("Handler:" + this.toString() + " - event{type:\"" + event.getType() + "\",\"" + event.attachment().toString() + "\"}"); printWriter.flush(); } public void onInitialize(CometEvent event) throws IOException { System.out.println("==== onInitialize ====="); } public void onTerminate(CometEvent event) throws IOException { System.out.println("==== onTerminate ====="); onInterrupt(event); } public void onInterrupt(CometEvent event) throws IOException{ System.out.println("==== onInterrupt ====="); CometEngine cometEngine = CometEngine.getEngine(); CometContext cometContext = cometEngine.getCometContext(contextPath); cometContext.removeCometHandler(this); } } The output in the browser is (text/plain): ---START--- Handler:CometResponseHandler@36b1bb - event{type:"1","155"} Handler:CometResponseHandler@36b1bb - event{type:"1","156"} Handler:CometResponseHandler@36b1bb - event{type:"1","156"} Handler:CometResponseHandler@36b1bb - event{type:"1","156"} Handler:CometResponseHandler@36b1bb - event{type:"1","156"} Handler:CometResponseHandler@36b1bb - event{type:"1","156"} Handler:CometResponseHandler@36b1bb - event{type:"1","157"} Handler:CometResponseHandler@36b1bb - event{type:"1","157"} Handler:CometResponseHandler@36b1bb - event{type:"1","157"} Handler:CometResponseHandler@36b1bb - event{type:"1","157"} Handler:CometResponseHandler@36b1bb - event{type:"1","157"} Handler:CometResponseHandler@36b1bb - event{type:"1","158"} Handler:CometResponseHandler@36b1bb - event{type:"1","158"} Handler:CometResponseHandler@36b1bb - event{type:"1","158"} Handler:CometResponseHandler@36b1bb - event{type:"1","158"} Handler:CometResponseHandler@36b1bb - event{type:"1","158"} Handler:CometResponseHandler@36b1bb - event{type:"1","159"} Handler:CometResponseHandler@36b1bb - event{type:"1","159"} Handler:CometResponseHandler@36b1bb - event{type:"1","159"} Handler:CometResponseHandler@36b1bb - event{type:"1","159"} The type is the CometEvent type (in this case a NOTIFY) As you can see the Integer passed as the attachment of the events apears repeated 4 times after the first onEvent. I know is a very poor code :) But it is just to prove the concept to start working in a Bayeux like protocol Am I doing somthing illegal with the API? Thnaks, Affonso

ops sorry for that ... How can I send you in a better way? Affonso

The problems happens because Servlet.init() is most probably invoked more that once by the WebContainer (more than one instance of the Servlet is created...now why 5 I need to look at the code :-) ). I recommend you add the handler only once by adding a flag to the session object. Let me know what you get.

Another thing: When the onTerminate and the onInterrupt of your api is called. I dont see the System.out.println("==== onInterrupt ====="); output in my server log. Shouldn't it be called by the ARP when , for example, the user hits the stop button in the browser? ps. I've installed the last Milestone Binary version available in the project site(9.1) and have updated the appserv-rt.jar with the updated CometEngine (with the fixes on the api) inside that jar. Can that be the problem?

onInterrupt will be called if your connection expires, e.g. the connection is idle more than 30 seconds. If you leave your connection opens without activity, the handler should be invoked. Thanks!

Hummm of course! Probably that! LOL That explains! Since: public CometContext register(String contextPath, int type){ CometContext cometContext = activeContexts.get(contextPath); if ( cometContext == null){ cometContext = cometContexts.poll(); if ( cometContext == null){ cometContext = new CometContext(contextPath,type); cometContext.setCometSelector(cometSelector); } activeContexts.put(contextPath,cometContext); } return cometContext; } returns the CometHandler that already exists tha output in the browser shows the same CometHandler instance. But why the container is starting more than one instance of the servlet? That should only be made for SingleThread model servlet? Isn't SingleThreadModel deprecated? Thanks a lot , Affonso

Yes, it is deprecated and I'm surprised you are geting this :-) My example isn't showing that behaviour...very strange :-) Let me investigate why (GlassFish may have a bug). I will update this blog soon! Thanks...I'm interested to see the result of your Bayeux-like protocol :-)

I did add the registration in the init() method and didn't get the even more than once, as expected. Is it possible to send me your app? I will debug to see if I can find something....

I've just put the therad as singleton and worked. Ok part of the concept is proved! ;) But I cant see onInitialize happening nor onTerminate and onInterrupt. Does it have somehing to do with the fact my comet handler is being registered with CometEngine.AFTER_SERVLET_PROCESSING ? When the client hit stop on the browser the ARP notifies somenthing to the CometHandler, or do I need to care about the print writer IO execptions to handle the client closing myself? Affonso

Sure ... but the code is already there. Is just this test app above.

The problem is not the register being called 5 times on init since the register method returs the same context if a context with the same context path already exists. The problem were the threads being created for each 5 instances (or by init being called 5 times, I dont know)

Ok onInitialize is happening. It was a logging issue. But I dont understand yet how to handle the client closing the connection. I dont know if ARP ensure an event when the client closes the connection or if I need to take care of that myself. I'll continue trying to understand here ;) Anythink new I'll let you know. Thanks

Cool. If you are using a browser, HTTP 1.1 is used, which means keep-alive is used. The CometEngine will close the connection, by default, after 30 seconds of innactivity. You can change that value by calling: cometContext.setExpirationDelay(...); // In milliseconds. Just try a small value. The CometEngine will also notify you by calling CometHandler.onEvent(), where the CometEvent is of type CometEvent.READ. Hope that help.

FYI, secure SSL Comet requests are now supported here

Hello Jeanfrancois, I pulled down Glassfish V2 build 16 and modified the domain.xml as you suggested. When I start the app server and try to asadmin list, I get this stack trace [#|2006-09-07T16:09:44.344-0400|INFO|sun-appserver-pe9.1|javax.enterprise.resource.webcontainer.jsf.config|_ThreadID=11;_ThreadName=Thread-5;/asadmin;|Completed initializing Sun's JavaServer Faces implementation (1.2_01-b04-FCS) for context '/asadmin'|#] [#|2006-09-07T16:09:45.077-0400|WARNING|sun-appserver-pe9.1|javax.enterprise.system.stream.err|_ThreadID=11;_ThreadName=Thread-5;_RequestID=c5842eff-d17f-4bc3-acae-2da1c24ef089;|Exception in thread "Thread-5" |#] [#|2006-09-07T16:09:45.084-0400|WARNING|sun-appserver-pe9.1|javax.enterprise.system.stream.err|_ThreadID=11;_ThreadName=Thread-5;_RequestID=c5842eff-d17f-4bc3-acae-2da1c24ef089;|java.lang.Error: Untranslated exception at sun.nio.ch.Net.translateToSocketException(Net.java:63) at sun.nio.ch.Net.translateException(Net.java:79) at sun.nio.ch.Net.translateException(Net.java:85) at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:61) at com.sun.enterprise.server.ss.provider.ASServerSocket.bind(ASServerSocket.java:292) at com.sun.enterprise.web.connector.grizzly.SelectorThread.initEndpoint(SelectorThread.java:625) at com.sun.enterprise.web.connector.grizzly.GrizzlyHttpProtocol.init(GrizzlyHttpProtocol.java:160) at org.apache.coyote.tomcat5.CoyoteConnector.initialize(CoyoteConnector.java:1581) at com.sun.enterprise.web.connector.coyote.PECoyoteConnector.initialize(PECoyoteConnector.java:760) at org.apache.catalina.startup.Embedded.start(Embedded.java:925) at com.sun.enterprise.web.WebContainer.start(WebContainer.java:811) at com.sun.enterprise.web.PEWebContainer.startInstance(PEWebContainer.java:763) at com.sun.enterprise.web.PEWebContainerLifecycle.onStartup(PEWebContainerLifecycle.java:72) at com.sun.enterprise.server.ondemand.ServiceGroup.startLifecycleServices(ServiceGroup.java:266) at com.sun.enterprise.server.ondemand.WebServiceGroup.startLifecycleServices(WebServiceGroup.java:210) at com.sun.enterprise.server.ondemand.WebServiceGroup.start(WebServiceGroup.java:60) at com.sun.enterprise.server.ondemand.ServiceGroup$1.run(ServiceGroup.java:180) at java.security.AccessController.doPrivileged(Native Method) at com.sun.enterprise.server.ondemand.ServiceGroup.startChildren(ServiceGroup.java:177) at com.sun.enterprise.server.ondemand.MainServiceGroup.start(MainServiceGroup.java:45) at com.sun.enterprise.server.ondemand.ServerEntryListenerImpl.notifyEntry(ServerEntryListenerImpl.java:72) at com.sun.enterprise.server.ondemand.entry.ServerEntryHelper.sendEvent(ServerEntryHelper.java:62) at com.sun.enterprise.server.ondemand.entry.ServerEntryHelper.generatePortEntryContext(ServerEntryHelper.java:43) at com.sun.enterprise.server.ss.ASSocketService.generateEntryContext(ASSocketService.java:279) at com.sun.enterprise.server.ss.ASSocketService$EntryPointThread.run(ASSocketService.java:525) Caused by: java.net.SocketException: Already bound at sun.nio.ch.Net.translateToSocketException(Net.java:49) ... 24 more Caused by: sun.nio.ch.AlreadyBoundException at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:114) at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:59) ... 21 more |#]

Strange. Seems the port is already in use. If you add in glassfish.home/domains/domain1/conf.domain.xml the following line <jvm-options>-Dcom.sun.enterprise.server.ss.ASQuickStartup=false</jvm-options> does it work.? Thanks! -- Jeanfrancois

Yes, that at least allowed asadmin list to work. I'll keep you posted. Ed

Hi Jean-Francois, thanks so much for this implementation, my team is currently embarking into the glassfish world and we're especially excited about the comet prospects. We're also looking at using jmaki, and are hence doubly interested in your jmaki chat implementation using grizzly comet. Any time frame on when it will be ready to share? Thanks and regards - Chris

Yes, I should make it available once Greg (jmaki lead) fix some of my bad Javascript :)

Jean-Francois- I am running into an issue with using Comet in build 22. I've gone ahead and added the Comet property to the domain.xml (property name="cometSupport" value="true") however am getting the following stacktrace when I try and access the Comet logic. (Previously this was not happening with build 19 (M2) although I did have some other issues. Can you let me know if something has changed in the Comet configuration that we would need to know about? java.lang.IllegalStateException: Grizzly Comet hasn't been registered at com.sun.enterprise.web.connector.grizzly.comet.CometContext.addCometHandler(CometContext.java:183) at CometServlet.processRequest(CometServlet.java:74) at CometServlet.doGet(CometServlet.java:91) at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) ... Thanks.. Josh

Actually, I get this exception on the first request but subsequent requests succeed. I haven't looked deep into the code to figure out why the first request fails however it is working (more or less).

Hi Josh, two possibilities:
  • Is the CometEngine.register called inside your Servlet.init()? If not, then if you call CometEngine.register() then cometContext.addCometHandler(..), you will get the exception. Why, because the register was called after the Comet url mapping happenned and since the mapping failed (no context registered), your the Servlet is executed synchronously. That's why the next request isn't producing the exception. I will fix that restriction soon.
  • Is the contextPath passed to the CometEngine.register() the same as the one used when invoking your Servlet? This will also cause that exception because as above, the request is executed synchronously
I did add that check because some user where getting NPE before, and the cause was I've explained above. Does it make sense?Thanks!!Jeanfrancois

Jeanfrancois- Thanks so much for your quick response. Originally I had a servlet.init() which was calling the register (which also had the same contextPath as the servlet, is this both cases above?). At any rate, I moved the register to a new "register" servlet and now it works fine. I'll look further to see the logic behind this and BTW- I was previously getting that NPE in build 19 :) I had two other comments:
First, I am trying to make the http request long-lived, like an hour, so I call CometContext.setExpirationDelay(60*60*1000) and add a handler to the CometContext however after 30 seconds the handler's onInterrupt( ) gets called instead of the 1 hour like I thought I had set. Any thoughts on why I was not able to set the expiration delay? Is this the right approach for this case? Secondly, we have a bit of a different use case. We have a large number of clients however which are bound to the Context but we would like the ability to notify individual clients in some cases as well as the standard notifyAll. Currently when we do the notify and all handlers are invoked, we just ignore the messages which aren't associated with that handler/client combo but we'd like to be more efficient than that. We are thinking of making a modification to the CometContext.notify() to have some intelligence as to which handler to notify. Any suggestions on this scenario? Does this sound like something that might be useful to others? Again thanks for the help.. Josh

Hum, I can't reproduce the timeout issue you are getting. When are you setting the timeout? Being able to invoke individual clients seems a very good idea and easy to implement. Just a quick question: how will you find the clients you are looking at? You will need a way to know who is connected, right? Let's see if I can come with something....Thanks!Jeanfrancois

Hi, Jfarcand
I am reading the source code of Grizzly and have a question about Comet implementation in Grizzly: You mentioned in your blog: >>"In Grizzly ARP, the Comet request isn't holding a Thread, but instead polled using NIO Selector (unfortunately using the SelectionKey.attach(..) for now ;-))." Take the "Chat" application as the example, What is a "Comet Request"? Is it the request which hold a HTTPConnection for a long time and wait for other messages? Such request will not send any message at most of time, instead, it just receives messages from the server. In this case, the NIO Selector (Do you mean CometSelector?) will not get any information by "readyKeys = selector.selectedKeys()" because no coming data is sending to the server through this channel. If the "Comet Request" is considered as the message POST request, the CometSelector and CometTask don't have a chance to involve at all. Because every post Servlet may have a "cometContext.notify(message)" to notify all handlers of this new message. So I guess the "CometTask" and "CometSelector" are not for browsers, they are for standalone Java applications, right? Can you explain more on these two classes? Thanks and Best Regards
Wang Yu

Hi Wang Yu.Take the "Chat" application as the example, What is a "Comet Request"? Is it the request which hold a HTTPConnection for a long time and wait for other messages?Yes, this is what I call a Comet Request So I guess the "CometTask" and "CometSelector" are not for browsers, they are for standalone Java applications, right?Those objects are implementation details :-) They are used for handling an asynchronous request, in both GlassFish and Grizzly standalone. The CometSelector is used to handle the CometContext.setExpirationTime for every connection, and the CometTask is just a Grizzly internal to track when the connection is closed or the client push data. Those objects are Grizzly internal and should never be referenced from an application (anyway you cannot reach them).

Hi gseel, this blog is for Grizzly bundled in GlassFish, not the standalone 1.5 version (I will soon publish the new example). Can you try GlassFish and let me know the result? Thanks

The fourth wow power leveling latest game in wow power leveling Warcraft series is ‘wow power leveling’. Also known as wow power leveling, it represents a wow power leveling multiplayer online wow power leveling game, the best of wow power leveling kind. Initially, it was wow gold it be released in 2001, but wow powerleveling was delayed wow powerleveling 2004, thus wow powerleveling the 10 years ofwow powerleveling franchise of thiswow gold series. The world of warcraft power leveling was not world of warcraft power levelingfulfilling, because wow power levelproblems with wow power level server’s stability power leveling wow performance occurred, but power leveling wow game still power leveling wow a financial success powerleveling wow the most powerleveling wow game of its kind. The number cheap wow power leveling users that play Maple Story mesos, exceeds 8.5 MapleStory mesos, worldwide.As a form ms mesos,recognition for mesos,outstanding popularity, the game SilkRoad Gold, received aSRO Gold, of awards. Now the question eq2 plat, why is eq2 gold, game eq2 Platinum, popular? For anyoneEverQuest 2 Platinum, played the previous EverQuest 2 gold, and EverQuest 2 plat, already initiated lotro gold, the mysterious world lotr gold, the breathtaking Lord of the Rings online Gold, this Rolex Replica nothing but an Replica Rolex adventure that continues the story of ‘Warcraft III: Frozen Throne’, four years after conclusion, in the world of Azeroth. The game is online role-playing, the previous versions being online and offline strategy games. The major thrills and unique features are present as in every Blizzard game.

Hi JFarcand I'm just starting to get to grips with Comet so asking newbie like questions. I've taken your chat example, almost unmodified although the comet-1.5.0.jar seemed to be in a different package from your example. I'm now getting the "IllegalStateException: Grizzly Comet hasn't been registered" whilst using your code and your web.xml. Any suggestions as to what I'm doing that is dumb? Thanks

Hi Aaron, can you ask your question on users@grizzly.dev.java.net? Are you sure you have enabled comet properly? Follow up on users@ :-) Thanks!

Hi Jeanfrancois, seems to be abit late to post here.. well, i have taken the example and set it up in Glassfish V2ur1 but i keep getting "IllegalStateException: Grizzly Comet hasn't been registered" error. I double checked that the contextPath is the same but it is still not working. Do you have any idea? Thanks and Regards, Aaron

Hi JFarcand, I am novice to comet technique.. I am trying to run your "chat" example using web container with the help of grizzly-comet jar... When i tried to invoke the chat servlet, it throws, "java.lang.IllegalStateException: Grizzly Comet hasn't been registered" Am I doing wrong here?? Is there any another example for using grizzly-comet w/o glassfish.. means web server compatible..??? for Sun One Java Webserver...

hi, jfarcand When I making a comet project according to your comet example, and I am using the long-streaming style. but I come to encounter a strange problem!!! that is : the glassfish alway throw the following exception at irregular intervals : can you give me some suggestion as I am novice to glassfish and comet. SEVERE: doSelect exception java.nio.channels.CancelledKeyException at sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:55) at sun.nio.ch.SelectionKeyImpl.interestOps(SelectionKeyImpl.java:64) at com.sun.grizzly.TCPSelectorHandler.onReadInterest(TCPSelectorHandler.java:668) at com.sun.grizzly.Controller.doSelect(Controller.java:422) at com.sun.grizzly.SelectorHandlerRunner.run(SelectorHandlerRunner.java:82) at com.sun.grizzly.Controller.startSelectorHandlerRunner(Controller.java:1136) at com.sun.grizzly.Controller.start(Controller.java:943) at com.sun.grizzly.http.SelectorThread.startListener(SelectorThread.java:1178) at com.sun.grizzly.http.SelectorThread.run(SelectorThread.java:1045) at com.sun.grizzly.http.SelectorThread.startEndpoint(SelectorThread.java:1110) at com.sun.enterprise.v3.services.impl.GrizzlyServiceListener.start(GrizzlyServiceListener.java:91) at com.sun.enterprise.v3.services.impl.GrizzlyProxy$1.run(GrizzlyProxy.java:222)