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

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

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.

_uacct = “UA-3111670-1″;
urchinTracker();

technorati:

About these ads
Categories: Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 51 other followers

%d bloggers like this: