X

Technology insights, news and tips.

  • Sun
    December 4, 2009

An Example of Porting Grizzly Comet to Servlet 3.0

Grizzly Comet is a very powerful feature in GlassFish v2 and GlassFish v3.
It provides a framework for writing many interesting applications.
However, the same application will not run in other servlet containers (without Grizzly).
One can resolve this by porting the code to use asynchronous API in Servlet 3.0
as in the glassfish sample, asyn-request-war, code
AjaxCometServlet.java.
In this case, the application would be able to run in all Servlet 3.0 containers.

In this blog, I will discuss the former by using AjaxCometServlet.java.
Even though, some details are specified to the chat example, the principle is general.
There are two main steps:

Mark the servlet as asynchronous


One can achieve this by using one of the following:
  • specifying the asyncSupported in @WebServlet,
    for instance, @WebServlet(urlPatterns = {"/chat"}, asyncSupported = true)
  • adding <async-supported>true</async-supported> under corresponding <servlet> in web.xml

Modify the servlet code


ServletUsing Grizzly CometUsing Asynchronous API in Servlet 3.0
instance variablesString contextPath
  • Thread, notifierThread
  • Queue of AsyncContext, queueBlockingQueue of message, messageQueue

    Queue<AsyncContext> queue = new ConcurrentLinkedQueue<AsynContext>();
    BlockingQueue messageQueue = new LinkedBlockingQueue();

init method
  • register contextPath in CometEngine
  • set expiration time

    contextPath = config.getServletContext().getContextPath() + "/chat";
    CometContext context = CometEngine.getEngine().register(contextPath);
    context.setExpirationDelay(5 \* 60 \* 1000);

create and start a notifierThread to consume message from messageQueue

    Runnable notifierRunnable = new Runnable() {
    public void run() {
        ...
        cMessage = messageQueue.take();
        for (AsyncContext ac : queue) {
            try {
                PrintWriter acWriter = ac.getResponse().getWriter();
                acWriter.println(cMessage);
                acWriter.flush();
            } catch(IOException ex) {
                queue.remove(ac);
        ...
    };
    notifierThread = new Thread(notifierRunnable);
    notifierThread.start();

doGet method
  • create a CometHandler<PrintWriter>
  • attach the response writer to the CometHandler
  • register contextPath in CometEngine
  • add the CometHandlerto CometContext

    ... // code for ChatListenerHandler is omitted
    ChatListnerHandler handler = new ChatListnerHandler();
    handler.attach(writer);
    CometContext context = CometEngine.getEngine().register(contextPath);
    context.addCometHandler(handler)

  • create a AsyncContext
  • set timeout
  • create an AsyncListener and add to AsyncContext
  • add the AsyncContext to queue

    final AsyncContext ac = req.startAsync();
    ac.setTimeout(10 \* 60 \* 1000);
    ac.addListener(new AsyncListener() {
        ...
    };
    queue.add(ac);

doPost methodcall context.notify(message);put the message into the messageQueue
destroy method 
  • clean up the queue
  • interrupt the notifierThread

    queue.clear();
    notifierThread.interrupt();

Note that asynchronous API in Servlet 3.0 are, in fact, quite easy to use. In this scenario, some coding is need for the notification thread.
If you don't want to manage the thread that notify, you can take look at Atmosphere.

Join the discussion

Comments ( 2 )
  • kunal Monday, December 7, 2009

    I tried with servlet 3.0 on Glassfish v3.

    request.startAsync(), works fine with req1-rsp1 but doesn't seem to work with req1,req2 on the same tcp connection(pipeline). It gives an exception 'java.lang.IllegalStateException: startAsync already called' , this is probably because "startAsync()--Subsequent invocations of this method, or its overloaded variant, will return the same AsyncContext instance, reinitialized as appropriate."

    on each GET request I start a 5 sec timer and on timer expiry send do asyncCtxt.complete() and hence within these 5 seconds all GETs fail with 500 error.


  • Shing Wai Chan Thursday, December 10, 2009

    One need to call asyncCtxt.complete before calling req2.startAsync. Otherwise, we will have an error.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.