Dropwizard 0.7.0では組み込みWebサーバとしてJetty 9.0.7が使われており、Jettyであれば非Java EE7環境でもWebSocketが使えるはずだと思ったので、一番簡単なechoを作って試してみました。
build.gradle
まず、websocket-server
への依存関係を追加します。
compile 'org.eclipse.jetty.websocket:websocket-server:9.0.7.v20131107'
これで、推移的な依存関係により、websocket-common
、 websocket-client
、websocket-servlet
が追加されます。
WebSocketクラスの作成
@org.eclipse.jetty.websocket.api.annotations.WebSocket
アノテーションをつけたPOJOを作ります。
package example.websocket.echo; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; @WebSocket public class EchoWebSocket { @OnWebSocketConnect public void onConnect(Session session) { // do nothing } @OnWebSocketMessage public void onText(Session session, String message) { session.getRemote().sendStringByFuture(message); } @OnWebSocketClose public void close(Session session, int statusCode, String reason) { // do nothing } }
今回のメインロジックですが、これだけです。メッセージを受け取った処理は@OnWebSocketMessageアノテーションをつけたメソッドで実装し、受け取ったメッセージをそのまま同じクライアントに送るようにしています。
ちなみに、このように@WebSocketクラスを作る方法と別の方法として、org.eclipse.jetty.websocket.api.WebSocketListener
インタフェースを実装する方法もあるみたいです。
アプリケーションクラスにWebSocketHandlerをセットする
io.dropwizard.Application
を継承して作ったアプリケーションクラスの run
メソッドを修正。
アプリケーションコンテキストにWebSocketHandlerを追加し、先ほど作った EchoWebSocket
クラスを登録します。
environment.getApplicationContext().setHandler(new WebSocketHandler() { @Override public void configure(WebSocketServletFactory factory) { factory.register(EchoWebSocket.class); } });
今回一番頭を悩ませたのがこの辺り。最初は WebSocketServlet
を作って登録させてみたのですが、うまく動かず、色々試行錯誤した結果、この方法に落ち着きました。
クライアントの作成
JavaScriptでオーソドックスに書きます。
var ws = new WebSocket("ws://" + location.host + "/"); ws.onopen = function(event) { console.log("connected."); ws.send("hello!"); } ws.onmessage = function(event) { console.log("message received from server."); console.log(event.data); }
HTMLでの見せ方は色々(省略)。
一通り書いたらあとはいつも通り ./gradlew jar
でビルドしてアプリケーションを起動後、 http://localhost:18080 にアクセスすれば、ブラウザのコンソールログとしてサーバからエコーバックされた "hello!" が出力されると思います。
補足
今回のJettyのバージョンは9.0.7ですが、9.1からは、JettyのWebSocket対応がJSR 356ベースに変わっているみたいです。逆に言うと、Jetty9.0.7のWebSocket対応はJava標準のものではなく、JettyのAPIに依存した形になっています。そのため、今後Dropwizardが対応するJettyのバージョンが9.1以上になれば、ここでの記事の情報は古くなります。
ちなみに最初はJSR 356の参照実装であるTyrusを試したのですが、断念しました。。