Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Asynchronous HTTP and Comet architectures

An introduction to asynchronous, non-blocking HTTP programming

  • Print
  • Feedback

Page 7 of 7

Comet in Jetty 6 and Tomcat 6

Comet support is one of the key features of the upcoming Servlet API 3.0. In the meantime, popular servlet engines already support Comet, using different approaches. Jetty 6 tries to keep the impact on the existing Servlet API programming approach to a minimum. Jetty uses continuations to avoid blocking threads by writing the response data. The continuation suspends the request and frees the current servlet request-handling thread. Jetty uses a RetryRequest runtime exception to implement the continuation.

When the suspend() method is called in Listing 16 below, Jetty throws a RetryRequest runtime exception. This RetryRequest exception is caught by the container, and the request placed into a timeout queue. If the timeout expires, the whole service method of the servlet will be re-executed. In this sense a Jetty continuation is not a "true" continuation: a classic continuation represents an execution state of a program at a certain point. The execution state is defined by the program's store and control state which includes the stack trace, variables, and program counter. Resuming the execution means that the program's store and control state will be reconstructed and the processing will continue. The Jetty approach implies that the service method is idempotence, which means it yields the same result after the method is applied multiple times.

Listing 16. Forever Frame -- Jetty-based server example

  public class ForeverFrameJettyServlet extends HttpServlet {

   public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    
      // handle the time request
      if (req.getRequestURI().endsWith("/time")) {
                        
         // get the jetty continuation
         Continuation cc = ContinuationSupport.getContinuation(req, null);
       
         // set the header
         resp.setContentType("text/html");
                            
         // write time periodically
         while (true) {
            cc.suspend(1000); // suspend the response
            String script = "<script>\r\n" +
                            "  parent.printTime(\"" + new Date() + "\");\r\n" +
                            "</script>";
            resp.getWriter().println(script);
            resp.getWriter().flush();
         }
      }

      // ...       
   }
}

Tomcat 6 introduces a new CometProcessor interface, which defines an event() callback method to process "Comet events" in an asynchronous way. The event() method will be called if specific events like BEGIN, ERROR, or READ occur. Implementing the CometProcessor interface means that Servlet API service methods such as doGet() will never be called.

Listing 17. Forever Frame -- Tomcat-based server example

public class ForeverFrameTomcatServlet extends HttpServlet implements CometProcessor  {
  
   private ArrayList<HttpServletResponse> connections = new ArrayList<HttpServletResponse>();
 
 
   public void init() throws ServletException {
      Thread timeSenderThread = new Thread(new TimeSender());
      timeSenderThread.setDaemon(true);
      timeSenderThread.start();
   }
 
         
   public void event(CometEvent event) throws IOException, ServletException {
      HttpServletRequest req = event.getHttpServletRequest();
      HttpServletResponse resp = event.getHttpServletResponse();
 
                 
      // handle the time request
      if (req.getRequestURI().endsWith("/time")) {

         if (event.getEventType() == CometEvent.EventType.BEGIN) {
            synchronized(connections) {
               connections.add(resp);
            }     
                            
         } else if (event.getEventType() == CometEvent.EventType.ERROR) {
             synchronized(connections) {
               connections.remove(resp);
            }
            resp.getWriter().close();
            event.close();
                                 
         } else if (event.getEventType() == CometEvent.EventType.END) {
            synchronized(connections) {
               connections.remove(resp);
            }
            resp.getWriter().close();
            event.close();                                  
         } 
      } 
      
      // ...
   }


   class TimeSender implements Runnable {
                 
      public void run() {
         while (true) {
            synchronized (connections) {
               for (HttpServletResponse response : connections) {
                  try {
                     PrintWriter bodyStream = response.getWriter();
                     String script = "<script>\r\n" +
                                     "  parent.printTime(\"" + new Date() + "\");\r\n" +
                                     "</script>";
                     bodyStream.write(script);
                     bodyStream.flush();
                  } catch (IOException e) {
                     // handle the exception
                  }
               }
            }
            try {
               Thread.sleep(1000);
            } catch (InterruptedException ignore) { }
         }
      }
   }
}

While beyond the scope of this article, it should also be noted that Grizzly, the NIO framework on which the Glassfish HTTP Listener is built, also handles Comet events. In contrast to Tomcat, Grizzly's Comet event handling is integrated with the servlet's service methods.

In conclusion

Servlet containers Tomcat and Jetty take two different approaches to asynchronous, non-blocking HTTP, but both provide adequate support. In addition, most popular HTTP and network libraries already support asynchronous message handling, at least at an early alpha-level implementation. The Apache Software Foundation's HttpCore library supports non-blocking HTTP for the client and server side, and is currently in beta status. The upcoming version of the Apache HttpClient is also based on the HttpCore library. The MINA project is currently working on Asyncweb, which also supports non-blocking HTTP on the client and server side. Examples in this article were based on the HTTP module of the xSocket network library, which is in alpha status.

See the Resources section to learn more about the technologies discussed in this article. You can also discuss them in the associated discussion forum.

About the author

Gregor Roth, creator of the xSocket network library, works as a software architect at United Internet group, a leading European Internet service provider to which GMX, 1&1, and Web.de belong. His areas of interest include software and system architecture, enterprise architecture management, object-oriented design, distributed computing and development methodologies. He began his professional career by writing C and Assembler-based microcontroller applications. In 1997 he started designing and developing large, distributed Java-based enterprise systems in the financial sector.

Read more about Enterprise Java in JavaWorld's Enterprise Java section.

  • Print
  • Feedback

Resources