[Java] WebSocket Applications using Java: JSR 356 Early Draft Now Available (TOTD #183)

原文はこちら。
https://blogs.oracle.com/arungupta/entry/websocket_applications_using_java_jsr

WebSocketは一つのTCP接続で全二重、双方向の通信を提供するプロトコルです。JSR 356はJava EE 7プラットフォームでWebSocketアプリケーションを作るための標準APIを定義しています。
JSR 356: JavaTM API for WebSocket
http://jcp.org/en/jsr/detail?id=356
このTip Of The Day (TOTD)では、WebSocketの概要と、JSRがどのように進化してプログラミングモデルをサポートするようになっているかをお伝えします。

まず、WebSocketの入門編から。

WebSocketとは、IETF RFC 6455 Protocol と W3C JavaScript API (still a Candidate Recommendation)を組み合わせたものです。このプロトコルは定義します ハンドシェイクとデータ転送の開通を定義します。APIを使って、WebページがWebSocketプロトコルによりリモートホストと双方向の通信が可能です。
The WebSocket Protocol
http://tools.ietf.org/html/rfc6455
The WebSocket API
http://www.w3.org/TR/websockets/
HTTPとは異なり、新たなTCP接続を作成する必要はありません。クライアントとサーバ間の各メッセージ交換のためにぎっしり詰め込んだヘッダーを送信する必要もありません。WebSocketプロトコルはTCP上の基本的なメッセージフレームを定義しています。最初のハンドシェイクがHTTP Upgradeを使って成立すると、他から独立してクライアントとサーバはお互いにメッセージを送信できます。リクエスト/レスポンスやクライアント・サーバ間の一方向の事前定義されたメッセージ交換パターンはなく、これらを基本的なプロトコル上で明示的に定義する必要があります。
Upgrade Header
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42
クライアントとサーバの間の通信はかなり対称的ですが、2つの違いがあります。
  • クライアントはWebSocketリクエストを待つサーバへの接続を開始する
  • クライアントはURIを使ってサーバに接続する。サーバは複数のクライアントからのリクエストを同じURIで待ち受けることができる。
この2個の違い以外は、ハンドシェイクを開通した後はクライアントとサーバは対称的な振る舞いをします。その意味で、「ピア」とみなされます。

ハンドシェイクに成功すると、クライアントとサーバはデータを「メッセージ」と呼ばれる概念的な単位で送受信します。メッセージは1個以上のフレームからなります。アプリケーションフレームにはアプリケーション用のペイロードが載っており、テキストデータもしくはバイナリデータの可能性があります。コントロールフレームにはプロトコルレベルの信号伝達のためのデータが載っています。

ではJSRについて説明しましょう。

Java Community ProcessでJSR 356としてJava API for WebSocketに取り組んでいます。ここではWebSocketアプリケーションを構築するための標準APIを定義します。このJSRは以下のようなサポートを提供します。
  • 双方向WebSocketの会話を処理するWebSocket Javaコンポーネントの作成
  • WebSocketイベントの開始、インターセプト
  • WebSocketのテキスト/バイナリメッセージの作成と利用
  • アプリケーションのためのWebSocketプロトコルとコンテンツモデルの定義機能
  • WebSocketセッションの構成および管理(タイムアウト、リトライ、クッキー、接続プーリングなど)
  • WebSocketアプリケーションがJava EEセキュリティモデル内で動作するための仕様
Tyrusは、JSR356の参照実装であり、すでにGlassFish 4.0 Promoted Buildに統合されています。
JSR 356: JavaTM API for WebSocket
http://www.jcp.org/en/jsr/detail?id=356
Java API for WebSocket
http://java.net/projects/websocket-spec/
Tyrus
http://tyrus.java.net/
GlassFish 4.0 (Promoted Build)
http://dlc.sun.com.edgesuite.net/glassfish/4.0/promoted/
では最後にコードをいくつか。

APIを使ってアノテーションやインターフェースを使いWebSocketエンドポイントを作成できます。このTOTDではアノテーションを使った簡単なサンプルをご紹介しましょう。将来のエントリでより進んだサンプルをご紹介する予定です。

@WebSocketEndpoint@WebSocketMessageを使ってPOJOをWebSocketエンドポイントに変換することができます。
@WebSocketEndpoint(path="/hello")
public class HelloBean {

    @WebSocketMessage
    public String sayHello(String name) {
        return "Hello " + name + "!";
    }
}
  • @WebSocketEndpointはこのクラスをpath属性で定義されたURIを待ち受けるWebSocketエンドポイントとしてマークします。
  • @WebSocketMessage はインバウンドのWebSocketメッセージを受信するメソッドを識別します。この最初のメソッドパラメータをインバウンドメッセージのペイロードに注入します。この場合、ペイロードはテキストベースを仮定しています。ペイロードがバイナリの場合、byte[] 型を取ることができます。@WebSocketEndpointdecoders属性を指定している場合、カスタムオブジェクトを指定できます。この属性はカスタムオブジェクトをデコードする方法を定義するクラスのリストを提供します。
  • このメソッドはオプションでSessionパラメータを取ることもできます。これはランタイムによって注入され、2個のエンドポイント間の会話をキャプチャします。
  • メソッドの返値の型は Stringbyte[]、その他カスタムオブジェクトの型を取ることができます。@WebSocketEndpoint のencoders属性はカスタムオブジェクトをエンコードする方法を定義するために必要です。
クライアント側は組み込みのJavaScriptを含むindex.jspです。JSPの本体は以下のようになっています。
コードは比較的単純です。HTMLフォームにはnameField.というテキストフィールドとボタンがあり、そのボタンから say_hello() メソッドを呼び出します。divプレースホルダは、出力表示に利用できます。

では、JavaScriptコードを見てみましょう。
<script language="javascript" type="text/javascript">
var wsUri = "ws://localhost:8080/HelloWebSocket/hello";
    var websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };

    function init() {
        output = document.getElementById("output");
    }

    function say_hello() {
     websocket.send(nameField.value);
        writeToScreen("SENT: " + nameField.value);
    }
  • このアプリケーションは、GlassFish 4.0 promoted build 57に "HelloWebSocket.war"(ダウンロードはこちら)としてデプロイされるので、WebSocketのエンドポイントは、"ws://localhost:8080/HelloWebSocket/hello"で待ち受けています。接続するためにURIを指定して、新しいWebSocket接続を開始します。
  • JavaScript APIはコールバックメソッドを定義しています。このコールバックメソッドは、接続が開通 (onOpen)したり、閉塞 (onClose)したり、エラーを受信(onError)したり、エンドポイントからメッセージを受信 (onMessage)したりしたときに呼び出されます。
  • クライアントAPIにはデータ送信のための様々なsendメソッドがあります。このスクリプトは、say_hello メソッドで先ほどご紹介したHTMLのnameFieldの値を使い、テキストデータを送信します。
  • ボタンをクリックする都度、WebSocket接続を介してエンドポイントにテキストボックスの内容を送信し、上記 sayHello メソッドに基づいた応答を受信します。
テストはこんな感じでやってみましょう。
  1. こちらからソースもしくはWARファイルをダウンロードする  
  2. GlassFish4.0 build 57それ以後をダウンロードして展開する
  3. "asadmin start-domain"でGlassFishを起動する
  4. "asadmin deploy HelloWebSocket.war"でWARファイルをデプロイする
  5. http://localhost:8080/HelloWebSocket/index.jspからアプリケーションにアクセス
"Say Hello" ボタンを押すと、以下のような出力になるはずです。

以下は参考資料です。
今後のエントリで以下のトピックについて説明する予定です(順序は不定です)。
  • ペイロードにバイナリデータを載せる場合
  • エンコーダ/デコーダを使うカスタムペイロード
  • エラー処理
  • インターフェースドリブンのWebSocketエンドポイント
  • Java client API
  • クライアントとサーバの設定
  • セキュリティ
  • サブプロトコル
  • 拡張機能
  • APIからのその他のトピック
  • 流れているWebSocketメッセージをキャプチャする

0 件のコメント:

コメントを投稿