Jettyで始めるWebSocket超入門

第4回 サーバ側の実装(後編)

この記事を読むのに必要な時間:およそ 2.5 分

今回は,前回に引き続き,Jettyを使ったWebSocketに対応したチャットアプリケーションのサーバ側の実装を行ないます。

MyWebSocketServlet

クライアントから接続要求があった時に,⁠WebSocket」インターフェイスの実装クラスを返す「Servlet」を作成する必要があります。

以下のように新規クラスを作成してください。

  • ソース・フォルダー:WebSocketChat/src/main/java
  • パッケージ:webSocketChat
  • 名前:MyWebSocketServlet
  • スーパークラス:org.eclipse.jetty.websocket.WebSocketServlet

クライアントから接続があった時に呼ばれる「doWebSocketConnect」メソッドを,⁠MyWebSocket」インスタンスを返すように修正します。

package webSocketChat;

import javax.servlet.http.HttpServletRequest;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;

public class MyWebSocketServlet extends WebSocketServlet {

  @Override
  protected WebSocket doWebSocketConnect(HttpServletRequest request,
      String protocol) {
    return new MyWebSocket();
  }

}

WebSocketChat

WebSocketChatの本体を実装します。次のように新規クラスを作成してください。

  • ソース・フォルダー:WebSocketChat/src/main/java
  • パッケージ:webSocketChat
  • 名前:WebSocketChat
  • スーパークラス:java.lang.Object
  • 「public static void main(String[] args)」にチェック

このクラスでは,サーバの作成・サーバに乗せるためのHandlerの作成と設定を行ない,サーバを起動します。コード全体を示した後に詳しく説明します。

package webSocketChat;

import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class WebSocketChat {

  public static void main(String[] args) throws Exception {
    new WebSocketChat();
  }

  public WebSocketChat() throws Exception {

    Server server = new Server(8040);

    ResourceHandler rh = new ResourceHandler();
    rh.setResourceBase(this.getClass().getClassLoader().getResource("html").toExternalForm());

    MyWebSocketServlet wss = new MyWebSocketServlet();
    ServletHolder sh = new ServletHolder(wss);
    ServletContextHandler sch = new ServletContextHandler();
    sch.addServlet(sh, "/ws/*");

    HandlerList hl = new HandlerList();
    hl.setHandlers(new Handler[] {rh, sch});
    server.setHandler(hl);

    server.start();

  }

}

それでは,それぞれの処理内容を見ていきましょう。

    Server server = new Server(8040);

ポート番号を指定しJettyのサーバをインスタンス化しています。ここまでに作成したMyWebSocketServletは,Handlerとしてサーバに登録する必要があります。同様に,HTTPでアクセスできるようにするためにResourceHanderも必要です。

まず,ResourceHandlerを先に準備します。ResourceHandlerは以下のようになっています。

    ResourceHandler rh = new ResourceHandler();
    rh.setResourceBase(this.getClass().getClassLoader().getResource("html").toExternalForm());

setResourceBaseの引数には,HTMLファイル等のクライアントで使用するリソースへのパスを指定しています。リソースは,Mavenでプロジェクトを作成した時に作られる「src/main/resources」内に配置します。ここはプログラム全体からアクセスが可能なため,⁠resources」ディレクトリ内に「html」ディレクトリを作成し,この中にプラウザ向け,つまりHTTPでアクセスされた時に使用されるファイルを格納します※1)⁠そして,⁠this.getClass().getClassLoader().getResource("html")」「html」ディレクトリへの「URL」オブジェクトを取得し文字列に変換しています※2)⁠

以上で,ResourceHandlerの準備ができました。

次は,MyWebSocketServletです。

    MyWebSocketServlet wss = new MyWebSocketServlet();
    ServletHolder sh = new ServletHolder(wss);
    ServletContextHandler sch = new ServletContextHandler();
    sch.addServlet(sh, "/ws/*");

ServletをHandlerとして登録する必要があると言及しましたが,ServletをそのままHandlerにすることはできません。そこで,MyWebSocketServletをまず「ServletHolder」オブジェクトでラップした後,⁠ServletContextHandler」に追加しています。追加の時に,⁠/ws/」にマッピングしています※3)⁠

次に,ServerオブジェクトにHandlerを登録します。バージョン6のJettyのServerオブジェクトは,Handlerを引数にとる「setHandler」「addHandler」メソッド,Handlerの配列を引数にとる「setHandlers」メソッドを持っていました。ところが,バージョン7ではHandlerを登録するメソッドは「setHandler」しかありません。Jetty7では次のようにすると複数登録できます。

    HandlerList hl = new HandlerList();
    hl.setHandlers(new Handler[] {rh, sch});
    server.setHandler(hl);

つまり,⁠Handler」インターフェイスを実装した「HandlerList」インスタンスに対しHandlerを複数追加し,それをサーバに設定します※4)⁠

最後にサーバを起動します。

    server.start();
※1
実行した時に,⁠html」ディレクトリがないとエラーが発生し起動できません。⁠html」ディレクトリの作成は次回行ないますが,ここで作成しておいても構いません。また,プラウザから正常にアクセスできるか確認したい場合は,⁠html」ディレクトリ内に空の「index.html」ファイルを作成しておいても良いでしょう。
※2
余談ですが,⁠resources」ディレクトリそのもののURLを取得したい場合は,⁠this.getClass().getClassLoader().getResource(".")」と記述します。
※3
つまり,WebSocketプロトコルでWebSocketサーバにアクセスするためのURLは,ホストがlocalhostの場合「ws://localhost/ws/」となります。
※4
「HandlerList」には「addHandler」メソッドも定義されています。これらのメソッドは「HandlerCollection」から継承しています。

次回予告

次回は,クライアント側の実装を解説します。

著者プロフィール

金城雄(きんじょうゆう)

NTTアドバンステクノロジ株式会社 アプリケーションソリューション事業本部 情報機器テクノロジセンタ所属。

Webテクノロジに関心を寄せるJavaScript好きのプログラマ。

コメント

  • import javax.servlet.http.HttpServletRequest;

    面白い、記事を本当にありがとうございます。
    第1回から一個一個やってきましたが、
    第4回でこけてしまいました。

    import javax.servlet.http.HttpServletRequest;
    のところで

    型 javax.servlet.http.HttpServletRequest を解決できません。必要な .class ファイルから間接的に参照されています MyWebSocketServlet.java /WebSocketChat/src/main/java/webSocketChat 行 1 Java 問題

    とエラーがでて、うまくいきません。

    pom.xmlは以下のようになっています。

    <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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>WebSocketChat</groupId>
    <artifactId>WebSocketChat</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <build>
    <plugins>
    <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>1.6</source>
    <target>1.6</target>
    <encoding>UTF-8</encoding>
    <debug>true</debug>
    <optimize>false</optimize>
    </configuration>
    </plugin>
    </plugins>
    </build>
    <dependencies>
    <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-webapp</artifactId>
    <version>7.1.4.v20100610</version>
    </dependency>
    <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-websocket</artifactId>
    <version>7.1.4.v20100610</version>
    </dependency>
    </dependencies>
    </project>

    アドバイスをよろしくお願いします。

    Commented : #1  ジョナサン (2012/05/23, 15:09)

コメントの記入