Java SEでWebSocketサーバを立てて遊ぶ

先だってリリースされたJava EE 7に JSR 356: Java API for WebSocket が入りました。 GlassFish v4などを利用すればWebSocketで遊べます。

が、やっぱJava SEで動かしたいですよね? ね?

というわけでJSR 356の参照実装であるTyrusを使います。

サーバ側のエンドポイントを作成します。 POJOにアノテーションを付ける感じです。

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/echo")
public class EchoServerEndPoint {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("[open] " + session);
    }

    @OnMessage
    public String onMessage(String message, Session session) {
        System.out.println("[" + message + "] " + session);
        return message;
    }

    @OnClose
    public void onClose(Session session) {
        System.out.println("[close] " + session);
    }
}

@OnOpenはセッションが確立したとき、@OnMessageはクライアントからメッセージが届いたとき、@OnCloseはセッションが切断されたときに呼ばれます。 @OnMessageを付けたメソッドではクライアントが送信したテキストをStringの引数で受け取ることができます。 なお、バイナリメッセージだとbyte[]やByteBufferで受け取れるようです。 また、このメソッドはStringを返していますがこれはクライアントへ送信されるテキストメッセージとなります。

では次にこれをJava SEで動かすためのコードを書きます。

import java.io.IOException;
import javax.websocket.DeploymentException;
import org.glassfish.tyrus.server.Server;

public class EchoMain {

    public static void main(String[] args) throws Exception {
        Server server = new Server("localhost", 8080, "/ws", EchoServerEndPoint.class);
        try {
            server.start();
            System.in.read();
        } finally {
            server.stop();
        }
    }
}

Serverのコンストラクタにホスト、ポート、ルートパス、エンドポイントのクラスを渡してインスタンス化し、startメソッドを呼べばWebSocketサーバの出来上がりです。 あとはクライアントから ws://localhost:8080/ws/echo に接続すればOKです。 簡単ですね。

DevLOVE関西「開発スターターキット」におけるJAX-RSの簡単な解説

どうも、GlassFishとJAX-RSを(・∀・)イイヨイイヨーと言っていた2番テーブルTAのうらがみです。

というわけで参加されたみなさん、お疲れ様でした。 いいよいいよと言いまくってた手前、今回のJAX-RSを利用していたソースコードをかるーく解説しておこうと思います。

まずはDevKanApplication.javaです。

@ApplicationPath("/services")
public class DevKanApplication extends Application {
}

Applicationを継承して@ApplicationPathで注釈していますが、このクラスがあればGlassFishさんが 「お、これはJAX-RSの出番やな( ー`дー´)キリッ」 という感じで認識してくれます。 なお@ApplicationPathに設定している /services が基点となるパスになります。

次いでCalculator.javaです。

@Path("/calc")
@Produces(MediaType.TEXT_PLAIN)
public class Calculator {

    @GET
    @Path("add")
    public String add(@QueryParam("a")int a, @QueryParam("b")int b){
        return "2";
    }
}

アノテーションがもりもり書かれていますが、これらはHTTPリクエスト/レスポンスに対応しています。 足し算のHTTPリクエスト/レスポンスはこんな感じになりますよー、っていうのを何となく書き出します。

リクエストはこんな感じ。

GET /devkan-calc/services/calc/add?a=2&b=3 HTTP/1.1

レスポンスはこんな感じ。

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 1

2

見比べるとよく分かりますが、

  • メソッドに書かれた@GETはリクエストメソッドに、
  • クラスとメソッドに書かれた@Pathはリクエストのパスに、
  • メソッドのパラメータに書かれた@QueryParamはリクエストのクエリパラメータに、
  • クラスに書かれた@ProducesはレスポンスのContent-Typeに、

それぞれ対応しています。 ついでに言うと、戻り値はレスポンスのエンティティボディに対応していますね。 HTTPを知っていればJAX-RSは使えるよー、という感じですね。

以上、JAX-RSの簡単な解説おわり。

WARを覗く

jar -tf build/libs/devkan-calc.war と打ってWARの中身を覗いてみましょう。 WARファイルの中身、WEB-INFディレクトリの下にあるのは DevKanApplication.class と Calculator.class だけです。 フレームワークのJARファイルはおろか、web.xmlすらありません。

JAX-RSはJava EEの一部であり、Java EEのアプリケーションサーバであるGlassFishにデプロイする場合はフレームワークのJARファイルは不要です。

また、最近ではサーブレットの登録にもweb.xmlは必須ではありません。 @WebServletというアノテーションで登録するか、ServletContextのaddServletメソッドで動的に登録できます。

この辺りはまた別の機会に詳しく紹介するとして(やらないフラグ)、大事なのは「JAX-RSなら2クラスでWebアプリ作れるですよ(`・ω・´)シャキーン」ということです。 お手軽ですね。

JAX-RSをもっと知りたい場合は

@btnrougeさん のブログ Programming Studio を読むと良いでしょう。 JAX-RSのタグが付けられたエントリをざくざく読んで行けばもりもり知識がつくと思います。

それと、手前味噌ですが、私もJAX-RSを一通り紹介するエントリを書きました。

ただし対象バージョンはちょっと古いです。 エントリは1.1。 最新は2.0。 そのうちエントリも2.0にアップデートしたいです(やらないフラグ)。

書籍なら「JavaによるRESTfulシステム構築」(ISBN:978-4873114675)が良いですかねー。 日本語訳が2010年に出版された書籍で、こちらも内容はJAX-RS 1系ですが今でも通用する情報が載っているとは思います。

そんな感じで、JAX-RSの回し者、うらがみでした(・∀・)