Cometを利用したサーブレットの例

それでは、早速Cometを利用したアプリケーションを作ってみよう。TomcatでComtを使用するには、HttpServletにおいてorg.apache.catalina.CometProcessorというインタフェースをimplementsする。このインタフェースにはeventというメソッドが宣言されている。CometProcessorをimplementsしたサーブレットにアクセスがあると、org.apache.catalina.CometEventというイベントが発生し、doGetやdoPostの代わりにeventメソッドにCometEventが渡されて呼び出される。

したがって基本的な形はリスト23のようになる。eventメソッド内にリクエストを処理するコードを記述しておけばよい。

リスト23 Cometを使うサーブレットプログラムの基本形

public class CometSampleServlet 
       extends HttpServlet  implements CometProcessor {
    public void event(CometEvent event) 
           throws IOException, ServletException {
        //ここに処理を記述する
    }
}

CometEventには次に示す4種類のタイプが定義されている。セッションを終了するにはCometEventのcloseメソッドを呼び出す。

  • EventType.BEGIN: コネクションが確率した際のイベント
  • EventType.END: リクエストの処理が終了した際のイベント
  • EventType.ERROR: I/Oエラーなど、何らかのエラーが発生した際のイベント
  • EventType.READ: データの入力があった場合のイベント

リスト24に、Cometを使用したサーブレットの例を示す。このサーブレットはリクエストを発行したブラウザに対して、コネクションを張ったまま5秒毎に時刻を送り続けるという処理をする。データの送信はMessageSenderというスレッドで行う。

リスト24 Cometを使うサーブレットの例(CometSampleServlet.java)

package sample;
 
import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.catalina.CometEvent;
import org.apache.catalina.CometProcessor;
 
public class CometSampleServlet 
        extends HttpServlet  implements CometProcessor {
    
    protected ArrayList<HttpServletResponse> connections = 
        new ArrayList<HttpServletResponse>();
    protected MessageSender sender = null;
 
    /**
     * アクセスがあった際に呼び出されるイベントハンドラ
     */
    public void event(CometEvent event) throws IOException, ServletException {
        HttpServletRequest request = event.getHttpServletRequest();
        HttpServletResponse response = event.getHttpServletResponse();

        if (event.getEventType() == CometEvent.EventType.BEGIN) {
            // データの送信を開始
            request.setAttribute("org.apache.tomcat.comet", Boolean.TRUE);
            PrintWriter writer = response.getWriter();
            writer.println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">");
            writer.println("<head><title>Comet Sample</title></head><body>");
            writer.flush();
            synchronized(connections) {
                connections.add(response);
            }
        }
        else if (event.getEventType() == CometEvent.EventType.ERROR) {
            // エラー発生時
            synchronized(connections) {
                connections.remove(response);
            }
            event.close();
        }
        else if (event.getEventType() == CometEvent.EventType.END) {
            // データの送信を終了
            synchronized(connections) {
                connections.remove(response);
            }
            PrintWriter writer = response.getWriter();
            writer.println("</body></html>");
            event.close();
        } 
        else if (event.getEventType() == CometEvent.EventType.READ) {
        }
    }
    
    /**
     * 初期化処理
     */
    public void init() throws ServletException {
        sender = new MessageSender();
        Thread senderThread = new Thread(sender);
        senderThread.setDaemon(true);
        senderThread.start();
    }
 
    /**
     * 終了処理
     */
    public void destroy() {
        connections.clear();
        sender.stop();
        sender = null;
    }
    
    /**
     * データ送信用のスレッド
     */
    public class MessageSender implements Runnable {
        // 後述
    }
}

まず、初期化段階でMessageSenderスレッドを起動して送信の準備を行う(initメソッド)。eventメソッドでは、BEGINイベントを受け取った場合にHTMLのヘッダを送信する。複数のコネクションを処理するために、受け取ったHttpResponseはリストに保持しておく。ENDイベントの場合はHTMLの閉じタグを送信してセッションを終了する。ERRORイベントでも同様にセッションを終了する。READイベントは今回は処理しない。