Eclipse + maven + jetty7でWebsocketを試す

Jetty7でWebsocketを使いたいと思って始めてみましたが、Jettyのデバッグが出来ず色々嵌りました。
もしかしたらJavaEEのバージョンをダウンロードすればもっと簡単にいけたのかもしれません。

また、今回はmavenを使いましたが必ず使う必要はありません。ただ、同時導入した結果です。
Maven無しでの構築の要望があれば記事書きます。。。

取り敢えずEclipseとプラグインまわりの準備から。



JAVA ・Eclipse

■ 説明
不要ですねw
■ インストール
インストーラーとかダウンロードして好きな所に展開してください。 Eclipseの日本語化とかは適宜導入して下さい。
■ 環境変数を設定
JAVA_HOME Javaのインストールフォルダ

Maven

■ 説明
Mavenはライブラリーファイルの管理を一元化するツール。 インターネット上のレポジトリからjarファイルをダウンロードして使えるので、便利。 初回のダウンロードは結構時間かかります。インターネット接続してないと使えません。 ローカルのレポジトリを使う手もありますが。
■ インストール
これもダウンロードして好きな所に展開。
■ 環境変数を設定
M2_HOME Mavenのインストールフォルダ
PATH %JAVA_HOME%bin; %M2_HOME%bin;
一応mavenのチェック コマンドプロンプトで

mvn –v
を実行。 動けばOKです。

M2Eclipse

■ 説明
Eclipseでmavenを使うとか、mvnで生成されたwar(Webサイト)プロジェクトを簡単に管理とか出来ます。
■ インストール
アップデートサイトhttp://m2eclipse.sonatype.org/sites/m2e/ から更新します。
■ プロジェクト作成
「ファイル」→「新規」→「maven」から「maven project」を選択。

Create a simple project (skip archetype selection)にチェックを入れる。
Workspaceは好きなところへ。
※自作したJetty7用プラグインの仕様の関係でプロジェクト名と同じにします。
次へを選択

GroupIdは通常、パッケージ名
ArtifactIdは通常、プロジェクト名
PackagingはWebなのでWarを選択

生成されるmaven + webappの構成はこんな感じ

○ src/main/javaフォルダ
mavenでのソースフォルダにあたる。

○ Src/main/webappフォルダ
Webでのトップフォルダにあたる。
以下WEB-INFフォルダとweb.xmlが不足しているため追加

○ Targetフォルダ
mvn packegeコマンドでビルドパッケージされたファイルはこちらに生成される(いわゆる本番稼動用ファイル)
Targetフォルダ内の生成フォルダはpom.xmlファイル(説明後述)によって変化する。

『ArtifactId - Version 』フォルダに
・ Classファイル
・ Src/main/webappの中身
・ 『ArtifactId - Version 』.warファイルが生成される。

ちなみにpom.xmlファイルを開くとこんな感じ

Eclipse上で『Add Dependency』を行うと自動的にpom.xmlは書き換えられる。


mvnコマンド to Eclipseプロジェクト

mvnのコマンドからやるとこんな感じになる。


cd /d @作りたいプロジェクトフォルダの親フォルダ
mvn archetype:create -DgroupId=グループ名 -DartifactId=プロジェクト名 -DarchetypeArtifactId=maven-archetype-webapp
cd @プロジェクトフォルダ
mvn eclipse:eclipse

Eclipseからプロジェクトのインポート

次のフォルダが無いのでソースフォルダとして追加
・src/main/java
・src/test/java
・src/test/resources

最後に「プロジェクトフォルダで右クリック」→「maven」→「Enable Dependency Management」」
junitがデフォルトでpom.xmlに設定されているので自動的にビルドパスへ追加されます。

ちなみにレポジトリからダウンロードされたjarファイルは
C:Documents and Settings@user.m2repositoryにあります。
Eclipseの「ウインドウ」→「設定」→「mven」→「User Settiongs」で確認出来ます。
環境変数M2_REPOがその在処かなと思います。
「ウインドウ」→「設定」→「Java」→「ビルド・パス」→「クラスパス変数」


サーブレット作成

■ 説明
WebSocketServletに行く前に普通のServletから。 jettyベースでサーブレットを作るのでjettyのライブラリーをmavenリポジトリからダウンロード(する設定を)します。
■ インストール
「プロジェクト右クリック」→「maven」から「Add Dependency」を選択 検索窓にorg.eclipse.jettyを入力すると一覧がズラッと表示されます。

ここでorg.eclipse.jetty jetty-webappを選択
なお、バージョンは7.2.0v20101020.jarを選択します。
※ 設置するサーバのバージョンに合わせるのがいいと思います。プラグインのバンドルは7.2.0

ここまで忘れていましたが、mavenで作ったプロジェクトはデフォルトでJava1.4なので変更しておきましょう。

「プロジェクト右クリック」→「maven」から「Add Plugin」を選択
検索窓にmaven-compiler-pluginを入力
任意のバージョンを選択。
次にpom.xmlファイルのconfigrationを編集します。


        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <encoding>UTF-8</encoding>
          <debug>true</debug>
          <optimize>false</optimize>
        </configuration>

「プロジェクト右クリック」→「maven」から「Update Project Configration」を選択します。


HttpServlet

■ 実装
シンプルで簡単なサーブレットを作りましょう

package k5m.websocket;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class SimpleServlet extends HttpServlet
{

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
	resp.getWriter().write(&quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;SimpleServlet&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;&quot;);
	
}

}

src/main/webapp/以下のweb.xmlにURLマッピングしましょう。


<web-app>
  <display-name>Websocket Web Application</display-name>
    <servlet>
    <servlet-name>SimpleServlet</servlet-name>
    <servlet-class>k5m.websocket.SimpleServlet</servlet-class>
    </servlet>
   
    <servlet-mapping>
    <servlet-name>SimpleServlet</servlet-name>
    <url-pattern>/simpleServlet</url-pattern>
    </servlet-mapping>
</web-app>

このあたりはTomcat等と同じですね。

あと、参考程度にwebapp以下にindex.htmlも配置しました。

では、Jettyサーバを起動。。。
っと、ここでまだJetty本体を配置していません。
Mavenでアプリケーションサーバも落としちゃいましょう。


Jettyプラグイン

■ 説明
アプリケーションサーバをmavenで管理
■ インストール
「プロジェクト右クリック」→「maven」から『Add Plugin』を選択

pom.xmlを開いてconfigurationに追加します


<plugin>
	<groupId>org.mortbay.jetty</groupId>
	<artifactId>jetty-maven-plugin</artifactId>
	<version>7.2.0.v20101020</version>
	<configuration>
		<scanIntervalSeconds>10</scanIntervalSeconds>
		<webAppConfig>
			<contextPath>/ </contextPath>
		</webAppConfig>
	</configuration>
</plugin>

ScanIntervalSecondsはどうやらjavaファイルを監視して再起動かけるための監視時間の値ですね。
ContextPathはlocalhost:8080/をあわらします。


<contextPath>/websocket </contextPath>

とすると、localhost:8080/websocketがマッピングされます。

■ 実行
コマンドプロンプトから

cd /d @プロジェクトフォルダ
mvn jetty:run
を実行します。

最初は色々とダウンロードが開始し、しばらくすると


[INFO] Started Jetty Server

の文字が表示されjettyが起動します。

http://localhost:8080/

http://localhost:8080/simpleservlet

終了はコマンドプロンプト画面で Ctrl + Cです。

この調子でWebsocektServletもと思うところですが、実はmvn jetty:runではwebsocketに対応していないようです。

参考
実際動かしてみましたがダメでした。これに気づくまでかなり時間がかかりました。 また、ブレークポイントをかけてデバッグをするための方法も分からずじまいでした。

今の所jetty:runでは開発しにくい状況なので次の方法で対応します。。


werkzeugkasten WebLauncherプラグイン

■ 説明
werkzeugkasten WebLauncherプラグインというものがGoogleCodeにありJetty6に対応しています。 Sysdeo Tomcat Launcher Plugin のような使い方ですね。
■ インストール
◯ werkzeugkasten プラグイン アップデートサイト http://werkzeugkasten.googlecode.com/svn/trunk/werkzeugkasten.update/ からEcllipseへインストール

WebLauncherを選択


Jetty7用 拙作プラグイン

■ 説明
GoogleCodeのwerkzeugkastenプラグインのファイルを参考にJetty7に対応するプラグインを作りました。
■ インストール
werkzeugkasten.weblauncher.jetty7_*.*.*を展開後Pluginフォルダへコピーします。 Eclipseで「プロジェクトを右クリック」→「プロパティ」→「WebLauncher」を選択

Webサーバの項目にてJetty7.2が選択出来ればインストール完了です。

設定前にファイル一式をコンパイルします。
「プロジェクト右クリック」 → 「実行」から「mvn package」
実行するとtargetフォルダにファイルが生成されます。

jetty:runではtargetファイルは参照していないようですが、このプラグインではtargetフォルダのファイルを参照します。
次に「プロジェクト右クリック」→「リフレッシュ」

では、もう一度WebLauncherの設定画面へ
・ WebLauncherを使用する。へチェックを入れる。
・ Servlet2.5 + JSP2.1 (デフォルト)
・ WebサーバJetty7.2.0
・ コンテキスト名(プロジェクトフォルダ名)
現仕様ではコンテキスト名とプロジェクトフォルダ名は同じ必要があります。
動作ディレクトリはtargetフォルダ内のWebAppにあたるフォルダを選択します。

これでWebLauncherをつかうことができるので、ツールバーから『サーバを起動する』を選択します。

試しにSimpleServletクラスのdoGetメソッドにブレークポイントをかけてデバッグ出来ることを確認します。
http://localhost:8080/simpleservlet

できました。


WebSoketServlet

ようやくWebSocketServletの実装です。

チャットとかが分かりやすいと思います。
今回この記事を参考に作りました。感謝!

Jettyで始めるWebSocket超入門
http://gihyo.jp/dev/feature/01/websocket
詳細はリンク先を見ていただくとして、以下ピックアップ。
■ インストール
Websocketライブラリを追加します。 プロジェクト右クリック→mavenで『Add Dependency』を選択 org.eclipse.jetty jetty-websocketを選択します バージョンは7.2.0v20101020.jarを選択。
実装
WebSocketオブジェクトをクライアントとサーバで共有するには WebSocketServletを継承したChatWebSocketServletを実装して、共有するWebSocketオブジェクトを返す。

WebSocektインターフェースを実装したChatWebSocketを実装。
参考にした記事ではここでstaticなオブジェクトで各接続間のWebsocketを格納することですべての接続を連結し、チャットとして機能させているようです。

Jetty7.2.0ではサンプルのメソッドに加えてonFragmentが追加されている様子。
このあたり、策定段階なので色々変動しそうなので注意が必要。
・ onConnect クライアント接続時
・ onDisconnectクライアント切断時
・ onMessageクライアントからメッセージを受け取った時
・ onFragment ???

onConnect時クライアントへメッセージをPushするためのOutbound
(WebSocketConnection)を確保しておく。

記事中のWebSocketChat (Websocketチャットサーバのランチャークラス)はWebLauncheに任せます。
あとはWebSocketservletクラスのURLマッピングをWeb.xmlに記述します。


<web-app>
  <display-name>Websocket Web Application</display-name>
    <servlet>
    <servlet-name>SimpleServlet</servlet-name>
    <servlet-class>k5m.websocket.SimpleServlet</servlet-class>
    </servlet>
    
    <servlet>
    <servlet-name>WebSocketServlet</servlet-name>
    <servlet-class>k5m.websocket.ChatWebSocketServlet</servlet-class>
    </servlet>
	  
    <servlet-mapping>
    <servlet-name>SimpleServlet</servlet-name>
    <url-pattern>/simpleServlet</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
    <servlet-name>WebSocketServlet</servlet-name>
    <url-pattern>/ws/*</url-pattern>
    </servlet-mapping>
</web-app>

サンプルをほとんどそのまま使わせていただきました。

準備ができたらmvn packageを実行(よく忘れます)してサーバ起動。

入力と同時に反映されます。

ChatWebSocket.onMessageでブレークポイントをかけて入力。

これでデバッグもだいぶ楽にできそうです。