@ITでTomcat 6で実現! Ajaxを超える通信技術Cometという連載があって、その中でチャットを作っていたので 自分の環境でも作ってみた。 Cometについてはこちらを参照 使用した環境は JDK 1.6.0_02 Tomcat6.0.14 eclipse-jee-europa-fall2 完成後のディレクトリイメージはこんな感じ。 C:. │ .classpath │ .project │ ├─.settings │ org.eclipse.jdt.core.prefs │ org.eclipse.jst.common.project.facet.core.prefs │ org.eclipse.wst.common.component │ org.eclipse.wst.common.project.facet.core.xml │ ├─build │ └─classes │ └─chat │ CometServlet$MessageSender.class │ CometServlet.class │ ├─src │ └─chat │ CometServlet.java │ └─WebContent │ index.html │ ├─js │ common.js │ ├─jsp │ CometChat.jsp │ CometChatSender.jsp │ ├─META-INF │ MANIFEST.MF │ └─WEB-INF │ web.xml │ └─lib workspaceイメージはこんな感じ では、順に作成していきます。 1.プロジェクト作成 このとき、Target RuntimeにTomcat6.0を指定して、 サーバも一緒に作ってしまいましょう。 2.server.xml修正 [1.]で作成されたサーバのserver.xmlを 以下のように修正します。 修正前 <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> 修正後(※1) <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="10000" redirectPort="8443" scheme="http" /> 3.jsp作成、js作成(※2) WebContent配下に其々js,jspディレクトリを作成し common.js function createHttpRequest() { if(window.ActiveXObject){ try { // MSXML2 return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { // MSXML return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e2) { return null; } } } else if(window.XMLHttpRequest){ return new XMLHttpRequest(); } else { return null; } } function postMessage( user , message , fileName , async ) { var httpoj = createHttpRequest(); httpoj.open( 'POST' , fileName , async ); httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); httpoj.onreadystatechange = function() { if (httpoj.readyState==4) { on_loaded(httpoj); } } httpoj.send( 'user=' + user + '&message=' + message ); } function on_loaded(oj) { res = oj.responseText; } CometChat.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>TestCometChat</title> </head> <frameset rows="90%,10%"> <frame name="display_messages" src="../CometServlet"> <frame name="message_sender" src="CometChatSender.jsp"> </frameset> </html> CometChatSender.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>TestCometChat Sender</title> </head> <body> <form method="POST" action="--WEBBOT-SELF--"> ユーザ名:<input type="text" name="user" size="20"> メッセージ:<input type="text" name="message" size="60"> <input type="button" value="送信" onclick="postMessage(user.value, message.value, '../CometServlet', true)"> </form> </body> </html> 4.web.xml修正 以下の記述を追加します。 (eclipseでServletを作成すると自動で追加される。) <servlet> <description></description> <display-name>CometServlet</display-name> <servlet-name>CometServlet</servlet-name> <servlet-class>chat.CometServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CometServlet</servlet-name> <url-pattern>/CometServlet</url-pattern> </servlet-mapping> 5.サーブレット作成(※3) CometServlet.javaを作成します。 package chat; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.CometEvent; import org.apache.catalina.CometProcessor; public class CometServlet extends HttpServlet implements CometProcessor { private static final long serialVersionUID = 1L; private ArrayList<HttpServletResponse> connections = new ArrayList<HttpServletResponse>(); protected MessageSender messageSender = null; public void init() throws ServletException { messageSender = new MessageSender(); Thread messageSenderThread = new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]"); messageSenderThread.setDaemon(true); messageSenderThread.start(); } public void destroy() { connections.clear(); messageSender.stop(); messageSender = null; } /** * Process the given Comet event. * * @param event The Comet event that will be processed * @throws IOException * @throws ServletException */ public void event(CometEvent event) throws IOException, ServletException { // Note: There should really be two servlets in this example, to avoid // mixing Comet stuff with regular connection processing HttpServletRequest request = event.getHttpServletRequest(); HttpServletResponse response = event.getHttpServletResponse(); if ("POST".equals(request.getMethod())) { // 送信されたメッセージ String user = request.getParameter("user"); String message = request.getParameter("message"); // 全てのクライアントへ送信 messageSender.send(user, message); //response.sendRedirect("/jsp/CometChat.jsp"); event.close(); return; } if (event.getEventType() == CometEvent.EventType.BEGIN) { // コネクション確率 begin(event, request, response); } else if (event.getEventType() == CometEvent.EventType.ERROR) { error(event, request, response); } else if (event.getEventType() == CometEvent.EventType.END) { end(event, request, response); } else if (event.getEventType() == CometEvent.EventType.READ) { read(event, request, response); } } protected void begin(CometEvent event, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { log("Begin for session: " + request.getSession(true).getId()); // タイムアウトの設定 event.setTimeout(60 * 1000 * 30); response.setContentType("text/html;charset=UTF-8"); PrintWriter writer = response.getWriter(); writer.println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">"); writer.println("<html><head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">"); writer.flush(); addResponce(response); } protected void end(CometEvent event, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { log("End for session: " + request.getSession(true).getId()); removeResponce(response); PrintWriter writer = response.getWriter(); writer.println("</body></html>"); event.close(); } protected void error(CometEvent event, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { log("Error for session: " + request.getSession(true).getId()); removeResponce(response); event.close(); } protected void read(CometEvent event, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { InputStream is = request.getInputStream(); byte[] buf = new byte[512]; while (is.available() > 0) { log("Available: " + is.available()); int n = is.read(buf); if (n > 0) { log("Read " + n + " bytes: " + new String(buf, 0, n) + " for session: " + request.getSession(true).getId()); } else if (n < 0) { log("End of file: " + n); end(event, request, response); return; } } } protected synchronized void addResponce(HttpServletResponse response) { connections.add(response); } protected synchronized void removeResponce(HttpServletResponse response) { connections.remove(response); } /** * Poller class. */ public class MessageSender implements Runnable { protected boolean running = true; protected ArrayList<String> messages = new ArrayList<String>(); public MessageSender() { } public void stop() { running = false; } /** * Add specified socket and associated pool to the poller. The socket will * be added to a temporary array, and polled first after a maximum amount * of time equal to pollTime (in most cases, latency will be much lower, * however). * * @param socket to add to the poller */ public void send(String user, String message) { synchronized (messages) { messages.add("[" + user + "]: " + message); messages.notify(); System.out.println("[" + user + "]: " + message); } } /** * The background thread that listens for incoming TCP/IP connections and * hands them off to an appropriate processor. */ public void run() { // Loop until we receive a shutdown command while (running) { // Loop if endpoint is paused if (messages.size() == 0) { try { synchronized (messages) { messages.wait(); } } catch (InterruptedException e) { // Ignore } } synchronized (connections) { String[] pendingMessages = null; synchronized (messages) { pendingMessages = messages.toArray(new String[0]); messages.clear(); } for (int i = 0; i < connections.size(); i++) { try { PrintWriter writer = connections.get(i).getWriter(); for (int j = 0; j < pendingMessages.length; j++) { // FIXME: Add HTML filtering writer.println(pendingMessages[j] + " "); System.out.println(pendingMessages[j] + " "); } writer.flush(); } catch (IOException e) { log("IOExeption sending message", e); } } } } } } } 6.これでCometChat.jspを起動すれば、OK 課題 チャットのメッセージに入力して、送信ボタンを クリックしても直ぐには画面表示されない。 (何度か試すと表示されるようになる。) (※1)@ITには以下のようにかかれていたのだけれど、 設定するとサーバがタイムアウトになって起動しなかった。 <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="10000" redirectPort="8443" scheme="http" secure="false" /> secureの項目をはずしたら起動できた。 詳細など不明。(誰か教えて。) <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="8443" scheme="http" /> (※2)@ITにはon_loaded関数の記述が無かったので追加しました。 (※3)@ITのサーブレットは動かなかったので、 Tomcat6.0のサンプルや、インターオフィスさんのWeb開発日記 を参考に作成。 |
<< 前記事(2008/01/11) | トップへ | 後記事(2008/01/18)>> |
タイトル (本文) | ブログ名/日時 |
---|
内 容 | ニックネーム/日時 |
---|
<< 前記事(2008/01/11) | トップへ | 後記事(2008/01/18)>> |