Friday Dec 04, 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.

About

Shing Wai Chan

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today