とりあえずWebSocketとつなごうと必死だった。
が、そもそもjava servletのこともマトモに知らなかったので勉強した。
その記録。
まずは普通のHttpリクエストを受けられるようにして、servletに関して学ぼうと思った。
maven使っているので、とりあえずプロジェクト生成。
mvn archetype:generate
捕捉しておくと、以下の条件でプロジェクトを生成した。
- maven-archetype-webapp
- groupId = com.ymatsu.websample
- artifactId = websample
pom.xmlに以下を追記。※1
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <!-- 中略 --> <repositories> <repository> <id>scala-tools.org</id> <name>Scala-tools Maven2 Repository</name> <url>http://scala-tools.org/repo-releases</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>scala-tools.org</id> <name>Scala-tools Maven2 Repository</name> <url>http://scala-tools.org/repo-releases</url> </pluginRepository> </pluginRepositories> <dependencies> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- jetty --> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>7.0.1.v20091125</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlets</artifactId> <version>7.0.1.v20091125</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-webapp</artifactId> <version>7.0.1.v20091125</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-websocket</artifactId> <version>7.0.1.v20091125</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <sourceDirectory>src/main/scala</sourceDirectory> <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory> <testSourceDirectory>src/test/scala</testSourceDirectory> <finalName>websample</finalName> <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <webAppConfig> <contextPath>/</contextPath> </webAppConfig> </configuration> </plugin> <plugin> <groupId>org.scala-tools</groupId> <artifactId>maven-scala-plugin</artifactId> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
※1 あとで分かるのだが、jettyのバージョンでハマった。
そんでもって、scalaで記述するために必要なディレクトリ生成
mkdir -p src/main/scala/com/ymatsu/websample mkdir -p src/test/scala
コードはこんな感じのものを、src/main/scala/以下に適切なpathで配置。
package com.ymatsu.websample import javax.servlet.http._ class Demo extends HttpServlet { override def doGet(request: HttpServletRequest, response: HttpServletResponse) = response.getWriter().println("Hello World (From Scala!)") }
あとは、web.xmlをsrc/main/webapp/WEB-INF/に配置。
中身は以下。
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>Home</servlet-name> <servlet-class>com.ymatsu.websample.Demo</servlet-class> </servlet> <servlet-mapping> <servlet-name>Home</servlet-name> <url-pattern>/home</url-pattern> </servlet-mapping> </web-app>
mvn jetty:run
でサーバー起動。
http://localhost:8080/home にアクセスしてHello world的な物が表示されればおk!!
んで本題のWebSocket。
とりあえずここのコードを使わせていただいて動かしてみた。
package com.ymatsu.websample import scala.collection.mutable.Set import javax.servlet.http._ import org.eclipse.jetty.websocket._ import org.eclipse.jetty.websocket.WebSocket.Outbound class ChatServlet extends WebSocketServlet { val clients = Set.empty[ChatWebSocket] override def doGet(req:HttpServletRequest, res:HttpServletResponse ) = getServletContext.getNamedDispatcher("Home").forward(req, res) override def doWebSocketConnect(req:HttpServletRequest, protocol:String ) = new ChatWebSocket class ChatWebSocket extends WebSocket { var outbound:Outbound = _ override def onConnect(outbound:Outbound ) = { this.outbound = outbound clients += this onMessage( 0, "WebSocket is success!!!"); } override def onMessage(frame:Byte, data:Array[Byte], offset:Int, length:Int ) = {} override def onMessage(frame:Byte, data:String ) = clients.foreach{ c => c.outbound.sendMessage( frame, data ) } override def onDisconnect = clients -= this } }
そんでもってweb.xmlに適当なパスを追記。(今回はws://localhost:8080/chat)
また、src/main/webapp直下にindex.htmlとして以下のファイルを配置。
<?xml version='1.0' encoding='utf-8' ?> <html> <head> <title>websocket test</title> <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' type='text/javascript'></script> <script type='text/javascript'> var ws = new WebSocket("ws://localhost:8080/socket"); ws.onmessage = function(e){ trace(e.data); }; ws.onclose = function(){ log("ws closed"); }; ws.onopen = function(){ log('connected!!'); }; $(function(){ $('input#post').click(function(){ var name = $('input#name').val(); var mes = $('input#message').val(); ws.send(name+" : "+mes); $('input#message').val(""); }); }); function log(message){ trace("[log] "+message); }; function trace(message){ var mes_div = $('<div />').html(message); $('div#chat').prepend(mes_div); }; </script> </head> <body> <form id='message'> <input id='name' size='15' type='text' value='名前' /> <input id='message' size='80' type='text' value='Hello world!!' /> <input id='post' type='button' value='post' /> </form> <div id='chat'></div> </body> </html>
ここまで来ればあとはmvn jetty:run で動く!
と思っていた。
が!
が!
動かない。
ずっと原因を追っていくと、servletがwebsocketのリクエストをGetとしてしか受け取ってくれない!!!
なぜだ!
と思ったがふとWebSocketの仕様が頭に浮かんで調べてみると、案の定jettyのver7.0は現状のchromeのwebsocket仕様に追いついていなかったらしい。
なのでpom.xmlのjettyのバージョンを以下に書き換え。
各jettyのdependencyに関して
- <version>7.0.1.v20091125</version> + <version>7.5.4.v20111024</version>
pluginに関して
- <artifactId>maven-jetty-plugin</artifactId> + <artifactId>jetty-maven-plugin</artifactId> + <version>7.5.4.v20111024</version>
そんでバージョンに合わせてソースコードを以下のように編集。
package com.ymatsu.websample import scala.collection.mutable.Set import javax.servlet.http._ import org.eclipse.jetty.websocket._ import org.eclipse.jetty.websocket.WebSocket.Connection class App extends WebSocketServlet { val clients = Set.empty[ChatWebSocket] override def doGet(req:HttpServletRequest, res:HttpServletResponse ) = getServletContext.getNamedDispatcher("Home").forward(req, res) override def doWebSocketConnect(req:HttpServletRequest, protocol:String ) = new ChatWebSocket class ChatWebSocket extends WebSocket.OnTextMessage { var connection:Connection = _ override def onOpen(connection:Connection ) = { this.connection = connection clients += this onMessage("WebSocket is success!!!"); } override def onMessage(data:String ) = clients.foreach{ c => c.connection.sendMessage(data) } override def onClose(code:Int,message:String) = clients -= this } }
以上でやっとこさ動きましたとさ。
チャットとしてはこれでも十分動きますが、チャットルームとか欲しいし、保存したいのでもっと色々勉強。
次はMongoDBとの接続なりやってみようかなと思う次第。