パフォーマンスチューニングblog

株式会社インターオフィス

Cometを試してみる(その2)

Cometを利用してリアルタイムなチャットのシステムを作成してみます。

Eclipse3.3を起動して動的Webプロジェクトを作成します。
プロジェクト名をTomcat6Test としておきます。

test パッケージに TestCometServlet.java を作成します。

JAVA:
  1. package test;
  2.  
  3. import java.io.IOException;
  4. import java.io.PrintWriter;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7.  
  8. import javax.servlet.ServletException;
  9. import javax.servlet.http.HttpServlet;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12.  
  13. import org.apache.catalina.CometEvent;
  14. import org.apache.catalina.CometProcessor;
  15.  
  16. public class TestCometServlet extends HttpServlet  implements CometProcessor {
  17.  
  18. private static final long serialVersionUID = -7518449035898058102L;
  19.  
  20. private List<httpservletresponse> responses = new ArrayList<httpservletresponse>();</httpservletresponse></httpservletresponse>
  21.  
  22. public void event(CometEvent event) throws IOException, ServletException {
  23.  
  24. final HttpServletRequest request = event.getHttpServletRequest();
  25. final HttpServletResponse response = event.getHttpServletResponse();
  26.  
  27. if ("POST".equals(request.getMethod())) { // 送信されたメッセージ
  28. final String user = request.getParameter("user");
  29. final String message = request.getParameter("message");
  30. // 全てのクライアントへ送信
  31. sendMessage(user + "&gt; " + message + "
  32. ");
  33.  
  34. event.close();
  35. return;
  36. }
  37.  
  38. if (event.getEventType() == CometEvent.EventType.BEGIN) {    // コネクション確率
  39. // タイムアウトの設定
  40. event.setTimeout(60 * 1000 * 30);
  41.  
  42. response.setContentType("text/html;charset=UTF-8");
  43. final PrintWriter writer = response.getWriter();
  44. writer.println("");
  45. writer.println("");
  46. writer.println("");
  47. writer.flush();
  48.  
  49. addResponce(response);
  50.  
  51. } else if (event.getEventType() == CometEvent.EventType.READ) { // データ入力
  52. removeResponce(response);
  53.  
  54. final PrintWriter writer = response.getWriter();
  55. writer.println("");
  56.  
  57. event.close();
  58.  
  59. } else if (event.getEventType() == CometEvent.EventType.END) {    // リクエスト終了
  60. removeResponce(response);
  61.  
  62. final PrintWriter writer = response.getWriter();
  63. writer.println("");
  64.  
  65. event.close();
  66.  
  67. } else if (event.getEventType() == CometEvent.EventType.ERROR) {    // エラー
  68. removeResponce(response);
  69.  
  70. event.close();
  71. }
  72. }
  73.  
  74. protected synchronized void addResponce(HttpServletResponse response) {
  75. responses.add(response);
  76. }
  77.  
  78. protected synchronized void removeResponce(HttpServletResponse response) {
  79. responses.remove(response);
  80. }
  81.  
  82. protected void sendMessage(final String message) {
  83. for (int i = 0; ((responses != null) &amp;&amp; (i &lt;responses.size())); i++) {
  84. try {
  85. final PrintWriter printWriter = responses.get(i).getWriter();
  86. printWriter.println(message);
  87. printWriter.flush();
  88.  
  89. } catch (IOException e) {
  90. e.printStackTrace();
  91. }
  92. }
  93. }
  94.  
  95. }

初めにリクエストがPOSTかどうかを判定しています。POSTの場合はメッセージの送信としてsendMessageメソッドを呼び出しています。

BEGINイベント
GETで初めて接続があった際ここに来ます。ページのヘッダ等をクライアントへ返して、レスポンスはListに格納しておきます。
メッセージの送信が無いとすぐにタイムアウトになってしまうので、タイムアウトの設定を30分にしておきます。

sendMessageメソッド
Listに格納されたレスポンス全てにメッセージを返します。接続のあったクライアント全てに送信される事になります。

WebCotent/jsp に TestCometChat.jsp を作成します。

JAVA:
  1. &lt;%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding="UTF-8"%&gt;
  3.  
  4.  
  5.  
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  7.  
  8.  
  9.  
  10.  
  11. このページにはフレームが使用されていますが、お使いのブラウザではサポートされていません。

フレーム上部はメッセージ送信、下部は受信用になります。
下部にてTestCometServletにGETしています。

引き続いて送信用の TestCometChatSender.jsp になります。

JAVA:
  1. &lt;%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding="UTF-8"%&gt;
  3.  
  4.  
  5.  
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  7.  
  8. <script src="../js/TestCometChat.js" type="text/javascript"></script>  <form method="post" action="--WEBBOT-SELF--"> User:
  9. <input name="user" size="20" type="text" /> Message:
  10. <input name="message" size="60" type="text" /> <input value="送信" onclick="postMessage(user.value, message.value, '../TestCometServlet', true)" type="button" /> </form>

userとmessageをPOSTします。

ebCotent/js に TestCometChat.js を作成します。

JavaScript:
  1. function createHttpRequest() {
  2.  
  3. if(window.ActiveXObject){
  4. try {
  5. //MSXML2
  6. return new ActiveXObject("Msxml2.XMLHTTP");
  7.  
  8. } catch (e) {
  9. try {
  10. //MSXML
  11. return new ActiveXObject("Microsoft.XMLHTTP");
  12.  
  13. } catch (e2) {
  14. return null;
  15. }
  16. }
  17.  
  18. } else if(window.XMLHttpRequest){
  19. return new XMLHttpRequest();
  20.  
  21. } else {
  22. return null;
  23. }
  24. }
  25.  
  26. function postMessage( user , message , fileName , async ) {
  27. var httpoj = createHttpRequest();
  28.  
  29. httpoj.open( 'POST' , fileName , async );
  30. httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
  31.  
  32. httpoj.onreadystatechange = function() {
  33. if (httpoj.readyState==4) {
  34. on_loaded(httpoj);
  35. }
  36. }
  37.  
  38. httpoj.send( 'user=' + user + '&amp;message=' + message )
  39. }
  40.  
  41. function on_loaded(oj) {
  42. res  = oj.responseText;
  43. //alert(res);
  44. }

TestCometChatSender.jsp が使用するJavaScriptになります。

web.xmlにサーブレットの設定を追加します。

XML:
  1. <servlet>
  2. <servlet-name>TestCometServlet</servlet-name>
  3. <servlet-class>test.TestCometServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>TestCometServlet</servlet-name>
  7. <url-pattern>/TestCometServlet</url-pattern>
  8. </servlet-mapping>

Tomcatを起動し、ページにアクセスしてみましょう。
http://localhost:8080/Tomcat6Test/jsp/TestCometChat.jsp

TestCometChat

実際にメッセージを入力してみます。

TestCometChat2

リアルタイムにメッセージの送信が行われているのが判ります。
(最初の送信ではバッファのフラッシュの問題なのか表示がうまくされませんが、何度か送信を行っていると表示される様になります。)

チャットシステムとしては不完全ですが、この様にCometを利用する事でサーバサイドのタイミングでデータを送信する事が出来るのが判ります。