Skip to main content

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

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

This blog has moved here

Related Topics >>

Comments

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)

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 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 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 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 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

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, 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

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

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

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

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).

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

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

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, that at least allowed asadmin list to work. I'll keep you posted. Ed

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

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 |#]

FYI, secure SSL Comet requests are now supported

here

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.

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

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)

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

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

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....

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 :-)

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

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!

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?

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.

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

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

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 :-)

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?

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

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.

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!!!!!

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 testing

Thanks

Jeanfrancois

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.

Thanks

Jeanfrancois

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); }

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?