とりあえず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との接続なりやってみようかなと思う次第。