| Oracle® Fusion Middleware Oracle WebLogic Server 12.1.3アプリケーションの開発 12c (12.1.3) E57574-04 |
|
![]() 前 |
![]() 次 |
WebLogic Server 12.1.3では、WebSocketプロトコル(RFC 6455)をサポートします。これは、TCPプロトコル経由で、2つのピア間の全二重通信を可能にします。WebLogic ServerにはWebSocketプロトコルとそれに付随するAPIが実装されているので、クライアントと双方向通信するアプリケーションを開発およびデプロイすることができます。いかなる種類のクライアント/サーバー通信にもWebSocketプロトコルを使用できますが、World Wide Web Consortium (W3C) JavaScript WebSocket APIを使用するWebページを実行するブラウザとの通信には、この実装が最もよく使用されます。WebLogic ServerにWebSocketプロトコルを実装することにより、Javaクライアントもサポートされます。
この章の内容は次のとおりです。
WebSocketとは、単一のTCP接続を介したクライアントとサーバー間の双方向同時通信を可能にするアプリケーション・プロトコルです。WebSocketプロトコルを使用すると、クライアントとサーバーはそれぞれ独立してデータを送信できます。HTML5仕様(http://www.w3.org/TR/html5/)の一部として、WebSocketプロトコルは、ほとんどのブラウザによってサポートされています。WebSocketプロトコルをサポートしているブラウザには、エンドポイントへの接続、メッセージの送信、およびWebSocketイベント(接続を開く、メッセージを受信、接続を閉じるなど)へのコールバック・メソッドの割当てを行うJavaScript APIが搭載されています。
WebSocketプロトコルの全般的な情報については、http://tools.ietf.org/html/rfc6455を参照してください。
HTTPで使用されている従来のリクエスト/レスポンス・モデルでは、クライアントがリソースをリクエストし、サーバーがレスポンスを返します。このやり取りは常にクライアントによって開始されるので、クライアントが先にリクエストを出さなければ、サーバーはデータを送信できません。このモデルは、変更頻度の低いドキュメントをクライアントが時々リクエストするような場合にWorld Wide Webで問題なく機能しましたが、コンテンツの更新が頻繁で、よりインタラクティブなWebエクスペリエンスが求められるようになるにしたがって、このアプローチの限界が次第に明らかになってきました。WebSocketプロトコルは、クライアントとサーバー間に全二重通信チャネルを提供することで、このような限界に取り組みます。WebSocketは、他のクライアント・テクノロジ(JavaScriptやHTML5など)と組み合せて、Webアプリケーションのユーザー・エクスペリエンスをより豊かにすることができます。
WebSocketアプリケーションでは、サーバーがWebSocketエンドポイントをパブリッシュし、クライアントがそのエンドポイントのURIを使用してサーバーに接続します。
WebSocketエンドポイントは、次に示すいずれかの形式のURIで表現されます。
ws://host:port/path?query wss://host:port/path?query
wsスキームは、暗号化されていないWebSocket接続を表します。
wssスキームは、暗号化されたWebSocket接続を表します。
これらの形式の残りの構成要素を次に示します。
[RFC3986]の3.2.2項で定義されているホスト。
オプション。[RFC3986]の3.2.3項で定義されているポート。デフォルトのポート番号は、80 (非暗号化接続の場合)および443 (暗号化接続の場合)です。
[RFC3986]の3.3項で定義されているパス。WebSocketエンドポイントのパスは、サーバー内のエンドポイントの場所を示します。
オプション。[RFC3986]の3.4項で定義されている問合せ。
WebSocket接続を開始するために、クライアントはサーバーがパブリッシュしたWebSocketエンドポイントにハンドシェイク・リクエストを送信します。クライアントは、エンドポイントのURIを使用してエンドポイントの場所を指定します。ハンドシェイク・リクエストが検証に合格し、サーバーがリクエストを受け入れると、接続が確立されます。ハンドシェイクは既存のHTTPベースのインフラストラクチャと互換性があります。つまり、WebサーバーではハンドシェイクをHTTP接続のアップグレード・リクエストとして解釈します。
例18-1は、クライアントからのハンドシェイク・リクエストを示しています。
例18-1 WebSocketクライアントからのハンドシェイク・リクエスト
GET /path/to/websocket/endpoint HTTP/1.1 Host: localhost Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg== Origin: http://localhost Sec-WebSocket-Version: 13
例18-2は、クライアントからのハンドシェイク・リクエストに対するサーバーからのハンドシェイク・レスポンスを示しています。
例18-2 WebSocketクライアントからのハンドシェイク・リクエストに対するサーバー・レスポンス
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
サーバーは、認識された操作をSec-WebSocket-Keyヘッダーの値に適用して、Sec-WebSocket-Acceptヘッダーの値を生成します。クライアントは、同じ操作をSec-WebSocket-Keyヘッダーの値に適用します。その結果がサーバーから受信した値と一致する場合、接続が正常に確立されます。ハンドシェイクに成功すると、クライアントとサーバーはメッセージを相互送信できます。
接続が確立されると、WebSocketプロトコルは対称性を示します。つまり、クライアントとWebLogic Serverインスタンスは、接続が開いている間はいつでもメッセージを相互送信可能で、その接続をいつでも閉じることができます。通常、クライアントは1つのサーバーとのみ接続しますが、サーバーは複数のクライアントから接続を受け入れます。
WebSocketは、テキスト・メッセージ(UTF-8でエンコードされたもの)とバイナリ・メッセージをサポートします。WebSocketの制御フレームは、close、pingおよびpong (pingフレームへのレスポンス)です。pingフレームとpongフレームには、アプリケーション・データが含まれている場合もあります。
WebLogic ServerのWebSocketの実装では、JSR 356 Java API for Websocketがサポートされます。Java API for WebSocketの詳細は、JSR 356の仕様を参照してください。
http://www.jcp.org/en/jsr/detail?id=356
|
注意: リリース12.1.2で導入された固有のWebLogic Server WebSocket APIは非推奨になりましたが、下位互換性のために引き続きサポートされます。JSR 356 Java API for WebSocketは固有のWebLogic Server WebSocket APIと共存できますが、アプリケーションに両方のAPIに対するコールを含めることはできません。アプリケーションで使用できるのは、一方のAPIのみです。 非推奨になったAPIの使用方法の詳細は、Oracle WebLogic Server 12c (12.1.2)のドキュメント(『Oracle Fusion Middleware Oracle WebLogic Serverアプリケーションの開発12c (12.1.2)』の第17章「WebLogic ServerでのWebSocketの使用」)を参照してください。 |
WebLogic ServerのWebSocketの実装には、次のコンポーネントが含まれています。
WebLogic ServerでのWebSocketプロトコルの実装は、JSR 356 Java API for WebSocketのリファレンス実装によって提供されます。このWebSocketプロトコルの実装では、接続アップグレードの処理、接続の確立と管理およびクライアントとのやり取りの処理を行います。
WebLogic WebSocket APIは、JSR 356 Java API for WebSocketのリファレンス実装によって提供されます。このAPIは次パッケージで構成されます。
javax.websocket.serverこのパッケージには、サーバーのエンドポイントを作成および構成するためのアノテーション、クラスおよびインタフェースが含まれています。
javax.websocketこのパッケージには、クライアントとサーバーのエンドポイントに共通のアノテーション、クラスおよびインタフェースが含まれています。
これらのパッケージのAPIリファレンスに関するドキュメントは、Java EE 7のAPI仕様の次の各項を参照してください。
プロトコル・フォールバックは、WebSocketプロトコルがサポートされていない場合に、WebSocketメッセージングの代替転送方法を使用するためのメカニズムを提供します。通常、WebSocketプロトコルがサポートされない理由は、WebSocketオブジェクトが使用できないためか、WebSocketフレームがファイアウォールによってブロックされているためです。このリリースでサポートされている代替転送方法は、HTTPロング・ポーリングのみです。
プロトコル・フォールバックを使用すると、実行時環境でWebSocketプロトコルがサポートされているかどうかにかかわらず、標準のプログラミングAPIを使用してWebSocketメッセージングを実行できます。詳細は、「WebSocketメッセージングのプロトコル・フォールバックの有効化」を参照してください。
WebLogic Serverのサンプル・コンポーネントをマシンにインストールして構成すると、WebSocketのサンプルを使用してWebLogic ServerでのWebSocketの使用方法を実際に確認できます。これらのサンプルの実行方法の詳細は、『Oracle WebLogic Serverの理解』の「サンプル・アプリケーションおよびサンプル・コード」を参照してください。
Java API for WebSocket (JSR-356)では、WebアプリケーションにWebSocketエンドポイントを作成、構成およびデプロイできます。また、JSR-356で指定されているWebSocketクライアントAPIを使用すると、任意のJavaアプリケーションからリモートのWebSocketエンドポイントにアクセスできます。
WebSocketエンドポイントの作成およびデプロイの手順を次に示します。
エンドポイント・クラスを作成します。
エンドポイントのライフサイクル・メソッドを実装します。
エンドポイントにビジネス・ロジックを追加します。
Webアプリケーション内にエンドポイントをデプロイします。
Java API for WebSocketでは、次の種類のエンドポイントを作成できます。
アノテーション付きエンドポイント
プログラム的なエンドポイント
プログラム的なエンドポイントとアノテーション付きエンドポイントでは手順が異なります。多くの場合、プログラム的なエンドポイントよりもアノテーション付きエンドポイントの方が簡単に作成およびデプロイできます。
|
注意: サーブレットとは対照的に、WebSocketエンドポイントは複数回インスタンス化されます。コンテナは、デプロイメントURIへの接続ごとに1つのエンドポイント・インスタンスを作成します。各インスタンスは1の接続のみに関連付けられます。この動作では、エンドポイント・インスタンスのコードを実行しているスレッドは常に1つのみなので、接続ごとにユーザーの状態を簡単に維持でき、デプロイメントが簡素化されます。 |
アノテーション付きエンドポイントを作成すると、エンドポイント・クラスのメソッドにアノテーションを付けることによって、WebSocket接続のライフサイクル・イベントを処理できます。詳細は、「アノテーション付きWebSocketエンドポイントでのライフサイクル・イベントの処理」を参照してください。アノテーション付きエンドポイントは、アプリケーションに自動的にデプロイされます。
Java API for WebSocketでは、アノテーション付きサーバー・エンドポイントとアノテーション付きクライアント・エンドポイントを作成できます。
アノテーション付きサーバー・エンドポイントの作成手順:
サーバー・エンドポイントを表すPlain Old Java Object (POJO)クラスを記述します。
このクラスには、引数を取らないパブリック・コンストラクタが必要です。
javax.websocket.server.ServerEndpointアノテーションでPOJOクラスのクラス宣言にアノテーションを付けます。
このアノテーションは、クラスがWebSocketサーバー・エンドポイントを表していることを示します。
ServerEndpointアノテーションのvalue要素を、エンドポイントのデプロイ先の相対パスに設定します。
パスはフォワード・スラッシュ(/)で開始する必要があります。
例18-3は、アノテーション付きサーバー・エンドポイント・クラスの宣言方法を示しています。プログラム的なエンドポイント・クラスを宣言して同じエンドポイントを表す方法の例は、例18-5を参照してください。
例18-3 アノテーション付きサーバー・エンドポイント・クラスの宣言
この例では、アノテーション付きサーバー・エンドポイント・クラスEchoEndpointを宣言します。エンドポイントは、アプリケーションからの相対パス/echoにデプロイされます。
import javax.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
...
}
アノテーション付きクライアント・エンドポイントの作成手順:
クライアント・エンドポイントを表すPlain Old Java Object (POJO)クラスを記述します。
このクラスは、引数を取るコンストラクタを持つことができます。ただし、このようなエンドポイントからサーバー・エンドポイントに接続するには、インスタンスを取得するconnectToServerメソッドの変数を使用する必要があります。クラスを取得する変数は使用できません。詳細は、「Java WebSocketクライアントのサーバー・エンドポイントへの接続」を参照してください。
avax.websocket.ClientEndpointアノテーションでPOJOクラスのクラス宣言にアノテーションを付けます。
このアノテーションは、クラスがWebSocketクライアント・エンドポイントを表していることを示します。
例18-4は、アノテーション付きクライアント・エンドポイント・クラスの宣言方法を示しています。
プログラム的なエンドポイントを作成するには、エンドポイントのスーパークラスのメソッドをオーバーライドして、WebSocket接続のライフサイクル・イベントを処理する必要があります。詳細は、「プログラム的なWebSocketエンドポイントでのライフサイクル・イベントの処理」を参照してください。プログラム的なエンドポイントは、アプリケーションに自動的にデプロイされません。エンドポイントは明示的にデプロイする必要があります。詳細は、「アプリケーション内でのプログラム的なエンドポイントへのパスの指定」を参照してください。
プログラム的なエンドポイントを作成するには、javax.websocket.Endpointクラスを拡張します。
例18-5は、プログラム的なエンドポイント・クラスの宣言方法を示しています。アノテーション付きエンドポイント・クラスを宣言して同じエンドポイントを表す方法の例は、例18-3を参照してください。
例18-5 プログラム的なエンドポイント・クラスの宣言
この例では、プログラム的なエンドポイント・クラスEchoEndpointを宣言します。アプリケーション内でこのエンドポイントへのパスを指定する方法を示した例は、例18-6を参照してください。
import javax.websocket.Endpoint;
...
public class EchoEndpoint extends Endpoint {
...
}
リモート・クライアントがプログラム的なエンドポイントに接続できるようにするには、アプリケーション内でエンドポイントへのパスを指定する必要があります。
アプリケーション内でプログラム的なエンドポイントへのパスを指定する手順:
javax.websocket.server.ServerEndpointConfig.Builder.create静的メソッドを呼び出して、javax.websocket.server.ServerEndpointConfig.Builderクラスのインスタンスを取得します。
createメソッドの呼出しでは、次の情報をパラメータとしてメソッドに渡します。
エンドポイントのクラス
エンドポイントを使用可能にするアプリケーションからの相対パス
前の手順で取得したServerEndpointConfig.Builderオブジェクト上で、buildメソッドを呼び出します。
アプリケーションをデプロイすると、エンドポイントが次のURIで使用可能になります。
ws://host:port/application/path
このURIの次の項目を置き換えます。
アプリケーションが稼働しているホスト。
WebLogic Serverのクライアント・リクエストのリスニング・ポート。
アプリケーションのデプロイ名。
createメソッドの呼出しで指定したパス。
たとえば、ローカル・ホスト上で稼働している/echoappアプリケーションからの相対パス/echoにあるエンドポイントのURIは、ws://localhost:8890/echoapp/echoです。
例18-6は、このタスクを1行のJavaコードで実行する方法を示しています。
例18-6 アプリケーション内でのプログラム的なエンドポイントへのパスの指定
この例では、アプリケーション内でのプログラム的なエンドポイントEchoEndpoint (例18-5で作成)へのパスとして、/echoを指定します。
import javax.websocket.server.ServerEndpointConfig.Builder; ... ServerEndpointConfig.Builder.create(EchoEndpoint.class, "/echo").build(); ...
WebSocket接続のライフサイクル・イベントの処理方法は、接続のエンドポイントがアノテーション付きエンドポイントかプログラム的なエンドポイントかによって異なります。詳細については、以下を参照してください。
アノテーション付きWebSocketでのライフサイクル・イベントの処理では、次のタスクを実行します。
イベントを処理するメソッドをエンドポイント・クラスに追加します。
メソッドで使用可能なパラメータは、イベントの指定に使用するアノテーションによって定義されます。
メソッドの処理対象であるイベントを指定するアノテーションを使用して、メソッドの宣言にアノテーションを付けます。
表18-1は、WebSocketエンドポイントのライフサイクル・イベントと、その処理メソッドを指定するためのアノテーション(javax.websocketパッケージで利用可能)を示しています。この表には、これらのメソッドで最もよく使用されるパラメータが例示されています。各例では、オプションのjavax.websocket.Sessionパラメータが使用されています。Sessionオブジェクトは、WebSocketエンドポイントのペア間の対話を表します。
アノテーションで使用可能なパラメータの組合せの詳細は、アノテーションのAPIリファレンスを参照してください。
表18-1 WebSocketエンドポイントのライフサイクル・イベントに使用するjavax.websocketのアノテーション
| イベント | アノテーション | 例 |
|---|---|---|
|
Connection opened |
@OnOpen
public void open(Session session,
EndpointConfig conf) { }
|
|
|
Message received |
@OnMessage
public String message (String msg) { }
|
|
|
Error |
@OnError
public void error(Session session,
Throwable error) { }
|
|
|
Connection closed |
@OnClose
public void close(Session session,
CloseReason reason) { }
|
Connection openedイベントを処理して、新しいWebSocketの対話が開始されたことをユーザーに通知します。
Connection openedイベントを処理するには、イベントを処理するメソッドにOnOpenアノテーションを付けます。
例18-7は、Connection openedイベントの処理方法を示しています。
Java API for WebSocketでは、次のタイプの受信メッセージを処理できます。
テキスト・メッセージ
バイナリ・メッセージ
Pongメッセージ
Message receivedイベントを処理するには、アプリケーションで受け取る受信メッセージのタイプごとに次の手順を実行します。
受信メッセージ・タイプを処理するメソッドをエンドポイント・クラスに追加します。
メッセージの受信に使用するパラメータのデータ型が、次の表に示したメッセージ・タイプと一致することを確認してください。
| メッセージ・タイプ | メッセージ受信用パラメータのデータ型 |
|---|---|
| テキスト | メッセージの受信方法に応じて、次のいずれかのデータ型が使用されます。
|
| バイナリ | メッセージの受信方法に応じて、次のいずれかのデータ型が使用されます。
|
| Pong | javax.websocket.PongMessage |
メソッドの宣言にOnMessageアノテーションを付けます。
エンドポイントでは最大で3つのメソッドに@OnMessageアノテーションを付けられます(テキスト、バイナリ、pongのメッセージ・タイプごとに1つずつ)。
例18-8は、アノテーション付きエンドポイントでの受信テキスト・メッセージの処理方法を示しています。
例18-8 アノテーション付きエンドポイントでの受信テキスト・メッセージの処理
この例では、このエンドポイントのピアにメッセージを返信することで、すべての受信テキスト・メッセージに応答します。OnMessageアノテーションの付いたメソッドはエンドポイント・クラスのメソッドで、個別のメッセージ・ハンドラ・クラスではありません。
プログラム的なエンドポイントで同じ操作を実行する方法の例は、例18-12を参照してください。
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
...
@OnMessage
public String onMessage(String msg) throws IOException {
return msg;
}
...
例18-9は、全タイプの受信メッセージの処理方法を示しています。
例18-9 全タイプの受信メッセージの処理方法
この例では、受信したテキスト・メッセージ、バイナリ・メッセージおよびpongメッセージを処理します。テキスト・メッセージは、Stringオブジェクトとしてメッセージ全体が受信されます。バイナリ・メッセージは、ByteBufferオブジェクトとしてメッセージ全体が受信されます。
import java.nio.ByteBuffer;
import javax.websocket.OnMessage;
import javax.websocket.PongMessage;
import javax.websocket.Session;
...
@OnMessage
public void textMessage(Session session, String msg) {
System.out.println("Text message: " + msg);
}
@OnMessage
public void binaryMessage(Session session, ByteBuffer msg) {
System.out.println("Binary message: " + msg.toString());
}
@OnMessage
public void pongMessage(Session session, PongMessage msg) {
System.out.println("Pong message: " +
msg.getApplicationData().toString());
}
...
処理する必要があるErrorイベントは、WebSocketプロトコルでモデル化されていないイベントのみです。次に例を示します。
接続時の問題
メッセージ・ハンドラからのランタイム・エラー
メッセージ・デコード時の変換エラー
Errorイベントを処理するには、イベント処理のメソッドにOnErrorアノテーションを付けます。
例18-10は、Errorイベントの処理方法を示しています。
Connection closedイベントを処理する必要があるのは、接続を閉じる前になんらかの特別な処理が必要な場合のみです(たとえば、接続が閉じられた後でデータを使用できなくなる前に、IDなどのセッション属性やセッションが保持している任意のアプリケーション・データを取得する場合など)。
Connection closedイベントを処理するには、イベント処理のメソッドにOnCloseアノテーションを付けます。
例18-11は、Connection closedイベントの処理方法を示しています。
表18-2は、プログラム的なWebSocketエンドポイントでのライフサイクル・イベントの処理方法をまとめたものです。
表18-2 プログラム的なWebSocketエンドポイントでのライフサイクル・イベントの処理
| イベント | 処理方法 |
|---|---|
|
Connection opened |
Endpoint |
|
Message received |
|
|
Error |
オプション: Endpoint このメソッドをオーバーライドしないと、エラー発生時に、エンドポイントが |
|
Connection closed |
オプション: Endpoint このメソッドをオーバーライドしないと、接続が閉じられる前に、エンドポイントが |
例18-12は、Connection openedイベントとMessage receivedイベントを処理して、プログラム的なエンドポイントで受信テキスト・メッセージを処理する方法を示しています。
例18-12 プログラム的なエンドポイントでの受信テキスト・メッセージの処理
この例では、すべての受信テキスト・メッセージをエコーします。この例では、EndpointクラスのonOpenメソッド(このクラスで唯一の抽象メソッド)をオーバーライドします。
Sessionパラメータは、このエンドポイントとリモートのエンドポイント間の対話を表します。addMessageHandlerメソッドはメッセージ・ハンドラを登録し、getBasicRemoteメソッドはリモート・エンドポイントを表すオブジェクトを返します。
メッセージ・ハンドラは、無名内部クラスとして実装されています。エンドポイントでテキスト・メッセージを受信すると、メッセージ・ハンドラのonMessageメソッドが呼び出されます。
メッセージの送信の詳細は、「メッセージの送信」を参照してください。
アノテーション付きエンドポイントで同じ操作を実行する方法の例は、例18-8を参照してください。
import java.io.IOException;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
...
@Override
public void onOpen(final Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) { ... }
}
});
}
...
Java API for WebSocketでは、Contexts and Dependency Injection (CDI)を使用して、WebSocketエンドポイントに必要なリソースを注入したり、リソースにアクセスしたりできます。注入されたリソースは、WebSocket接続のライフサイクル・イベントを処理するメソッド内から使用できます。
CDIの詳細は、第9章「Contexts and Dependency Injection for the Java EE platformの使用方法」を参照してください。
WebSocketエンドポイントでリソースを定義、注入およびアクセスする手順:
注入するリソースを表すマネージドBeanを定義します。
詳細は、「マネージドBeanの定義」を参照してください。
エンドポイント・クラスで、マネージドBeanを注入します。
詳細は、「Beanの注入」を参照してください。
必要に応じて、関連するメソッド内から、注入されたBeanのメソッドを呼び出します。
次の例では、WebSocketエンドポイントでリソースを定義、注入およびアクセスする方法を示します。
例18-13 WebSocketエンドポイントでのマネージドBeanの定義
この例では、マネージドBeanクラスInjectedSimpleBeanを定義します。
import javax.annotation.PostConstruct;
public class InjectedSimpleBean {
private static final String TEXT = " (from your server)";
private boolean postConstructCalled = false;
public String getText() {
return postConstructCalled ? TEXT : null;
}
@PostConstruct
public void postConstruct() {
postConstructCalled = true;
}
}
例18-14 WebSocketエンドポイントでのリソースの注入およびアクセス
この例では、InjectedSimpleBeanマネージドBeanクラスのインスタンスを、サーバー・エンドポイントSimpleEndpointに注入します。エンドポイントがメッセージを受信すると、注入されたBean上でgetTextメソッドが呼び出されます。メソッドからテキスト(sent from your server)が返されます。その後で、元のメッセージと収集されたデータの連結メッセージがエンドポイントから返信されます。
InjectedSimpleBeanマネージドBeanクラスは、例18-13で定義されています。
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
@ServerEndpoint(value = "/simple")
public class SimpleEndpoint {
private boolean postConstructCalled = false;
@Inject
InjectedSimpleBean bean;
@OnMessage
public String echo(String message) {
return postConstructCalled ?
String.format("%s%s", message, bean.getText()) :
"PostConstruct was not called";
}
@PostConstruct
public void postConstruct() {
postConstructCalled = true;
}
}
Java API for WebSocketでは、次のタイプのメッセージをエンドポイントからその接続ピアに送信できます。
テキスト・メッセージ
バイナリ・メッセージ
Pingフレーム
エンドポイントの単一ピアにメッセージを送信する手順:
接続のSessionオブジェクトを取得します。
Sessionオブジェクトは、エンドポイントのライフサイクル・メソッドのパラメータとして使用できます。このオブジェクトを取得する方法は、送信するメッセージがピアから受け取ったメッセージに対する応答であるかどうかによって異なります。
メッセージが応答である場合は、メッセージを受信したメソッド内からSessionオブジェクトを取得します。
メッセージが応答でない場合は、Connection openedイベントを処理するメソッドで、Sessionオブジェクトをエンドポイント・クラスのインスタンス変数として保存します。このように保存することで、他のメソッドからSessionオブジェクトにアクセスできるようになります。
Sessionオブジェクトを使用して、javax.websocket.RemoteEndpointのサブインタフェースのいずれかを実装するオブジェクトを取得します。
メッセージの同期送信を行う場合は、RemoteEndpoint.Basicオブジェクトを取得します。このオブジェクトでは、ブロッキング・メソッドでメッセージを送信できます。
RemoteEndpoint.Basicオブジェクトを取得するには、Session.getBasicRemote()メソッドを呼び出します。
メッセージの非同期送信を行う場合は、RemoteEndpoint.Asyncオブジェクトを取得します。このオブジェクトでは、非ブロッキング・メソッドでメッセージを送信できます。
RemoteEndpoint.Asyncオブジェクトを取得するには、Session.getAsyncRemote()メソッドを呼び出します。
前の手順で取得したRemoteEndpointオブジェクトを使用して、メッセージをピアに送信します。
次のリストでは、メッセージをピアに送信する場合に使用できるメソッドの一部を示します。
void RemoteEndpoint.Basic.sendText(String text)
テキスト・メッセージをピアに送信します。このメソッドは、メッセージ全体が送信されるまでブロックします。
void RemoteEndpoint.Basic.sendBinary(ByteBuffer data)
バイナリ・メッセージをピアに送信します。このメソッドは、メッセージ全体が送信されるまでブロックします。
void RemoteEndpoint.sendPing(ByteBuffer appData)
pingフレームをピアに送信します。
void RemoteEndpoint.sendPong(ByteBuffer appData)
pongフレームをピアに送信します。
例18-15では、この操作手順を使用して、すべての受信テキスト・メッセージに応答する方法を示します。メッセージをメソッドの戻り値として送信する方法の例は、例18-8を参照してください。
例18-15 エンドポイントの単一ピアへのメッセージ送信
この例では、このエンドポイントのピアにメッセージを返信することで、すべての受信テキスト・メッセージに応答します。
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
...
@OnMessage
public void onMessage(Session session, String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) { ... }
}
...
一部のWebSocketアプリケーションでは、アプリケーションのWebSocketエンドポイントのすべての接続ピアに対してメッセージを送信する必要があります。次に例を示します。
株式アプリケーションでは、すべての接続クライアントに株価情報を送信する必要があります。
チャット・アプリケーションでは、1人のユーザーからのメッセージを、同じチャット・ルーム内の他のすべてのクライアントに送信する必要があります。
オンライン・オークション・アプリケーションでは、アイテムのすべての入札者に最新の付け値を送信する必要があります。
しかし、エンドポイント・クラスの各インスタンスは1つの接続およびピアにのみ関連付けられます。したがって、エンドポイントのすべてのピアにメッセージを送信するには、同じエンドポイントへの接続を表す開いているWebSocketセッションすべてに対して、操作を繰り返す必要があります。
エンドポイントのすべてのピアにメッセージを送信する手順:
エンドポイントへの接続を表す開いているWebSocketセッションをすべて取得します。
そのためには、エンドポイントのSessionオブジェクト上でgetOpenSessionsメソッドを呼び出します。
前の手順で取得した開いているセッションそれぞれにメッセージを送信します。
セッションを使用して、RemoteEndpointオブジェクトを取得します。
RemoteEndpointオブジェクトを使用して、メッセージを送信します。
詳細は、「エンドポイントの単一ピアへのメッセージ送信」を参照してください。
例18-16 エンドポイントのすべてのピアへのメッセージ送信
この例では、受信テキスト・メッセージをすべての接続ピアに転送します。
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echoall")
public static class EchoAllEndpoint {
@OnMessage
public void messageReceived(Session session, String msg) {
for (Session sess : session.getOpenSessions()) {
try {
sess.getBasicRemote().sendText(msg);
} catch (IOException e) {
// handle exception
}
}
}
}
エンドポイントのすべてのピアにメッセージを送信する場合の効率性の確保
多数のメッセージが送信される実際のアプリケーションでは、複数のスレッドを使用することで、アプリケーションからメッセージを効率的に送信できるようになります。
開いているWebSocket接続が多すぎる場合、1つのスレッドを使用してメッセージをブロードキャストすることは非効率です。なぜなら、繰り返しプロセスにおいては、クライアントがメッセージを受信するのにかかる時間は、その場所に依存するからです。何千ものWebSocket接続が開いていると、繰り返しが遅くなるため、一部のクライアントはメッセージを早く受信するようになりますが、メッセージの受信がかなり後になるクライアントも出てきます。この遅れは、特定の状況では許容できません。たとえば、株式アプリケーションでは、各クライアントができるかぎり早く株価データを受信する必要があります。
効率を上げるために、アプリケーションで、開いているWebSocket接続を複数のグループに分割し、複数のスレッドを使用して、WebSocket接続の各グループにメッセージをブロードキャストすることができます。
Java API for WebSocketの仕様では、Java EE実装においてはエンドポイント・クラスを接続ごとに1回ずつインスタンス化することが求められています。この要件によって、WebSocketエンドポイント・クラスのコードを実行しているスレッドは常に1つのみであることが保証されるので、WebSocketエンドポイントのデプロイメントが容易になります。エンドポイントに新規スレッドを導入する場合は、複数のスレッドからアクセスされる変数およびメソッドがスレッド・セーフであることを保証する必要があります。
Java API for WebSocketでは、エンコーダとデコーダを使用して、WebSocketメッセージとカスタムJavaタイプ間での変換をサポートします。このメカニズムでは、オブジェクトのシリアライズとデシリアライズからビジネス・ロジックが切り離されるので、WebSocketアプリケーションがシンプルになります。
エンコーダはJavaオブジェクトを取得して、WebSocketのテキスト・メッセージまたはバイナリ・メッセージとして送信可能な表現を生成します。たとえば、エンコーダは通常、JavaScript Object Notation (JSON)表現、Extensible Markup Language (XML)表現またはバイナリ表現を生成します。デコーダは逆の役割を果します。つまり、WebSocketメッセージを読み取ってJavaオブジェクトを作成します。
|
注意: 複数のJavaタイプを同じタイプのWebSocketメッセージとして送受信する必要がある場合は、タイプの定義で共通クラスを拡張します。たとえば、JavaタイプMessageAおよびMessageBをテキスト・メッセージとして送受信する必要がある場合は、これらのタイプの定義で共通クラスMessageを拡張します。
このようにタイプを定義することにより、単一のデコーダ・クラスの実装で、複数のタイプに対応できます。 |
テキスト・メッセージ用エンコーダとバイナリ・メッセージ用エンコーダは、それぞれ複数指定できます。エンドポイントと同様に、エンコーダ・インスタンスは1つのWebSocket接続およびピアにのみ関連付けられます。したがって、エンドポイント・インスタンスのコードを実行しているスレッドは常に1つのみです。
JavaオブジェクトをWebSocketメッセージとしてエンコードする手順:
WebSocketメッセージとして送信するカスタムJavaタイプごとに、WebSocketメッセージのタイプに適したインタフェースを実装します。
テキスト・メッセージには、javax.websocket.Encoder.Text<T>を実装します。
バイナリ・メッセージには、javax.websocket.Encoder.Binary<T>を実装します。
これらのインタフェースはencodeメソッドを指定します。
エンドポイントでエンコーダ実装が使用されるように指定します。
アノテーション付きエンドポイントの場合は、エンコーダ実装の名前をServerEndpointアノテーションのencodersオプション要素に追加します。
プログラム的なエンドポイントの場合は、エンコーダ実装の名前のリストをjavax.websocket.server.ServerEndpointConfig.Builderオブジェクトのencodersメソッドのパラメータとして渡します。
RemoteEndpoint.BasicまたはRemoteEndpoint.AsyncインタフェースのsendObject(Object data)メソッドを使用して、オブジェクトをメッセージとして送信します。
コンテナはタイプの一致するエンコーダを検索し、それを使用してオブジェクトをWebSocketメッセージに変換します。
次の例では、Javaタイプcom.example.game.message.MessageAおよびcom.example.game.message.MessageBをテキスト・メッセージとして送信する方法を示します。
例18-17 エンコーダ・インタフェースの実装
この例では、Encoder.Text<MessageA>インタフェースを実装します。
package com.example.game.encoder;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import com.example.game.message.MessageA;
...
public class MessageATextEncoder implements Encoder.Text<MessageA> {
@Override
public void init(EndpointConfig ec) { }
@Override
public void destroy() { }
@Override
public String encode(MessageA msgA) throws EncodeException {
// Access msgA's properties and convert to JSON text...
return msgAJsonString;
}
...
}
Encoder.Text<MessageB>を実装する場合も同様です。
例18-18 アノテーション付きWebSocketエンドポイント用のエンコーダの定義
この例では、エンコーダ・クラスMessageATextEncoder.classおよびMessageBTextEncoder.classを、WebSocketサーバー・エンドポイントEncEndpointに定義します。
package com.example.game;
import javax.websocket.server.ServerEndpoint;
import com.example.game.encoder.MessageATextEncoder;
import com.example.game.encoder.MessageBTextEncoder;
...
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class }
...
)
public class EncEndpoint { ... }
例18-19 WebSocketメッセージとしてエンコードされたJavaオブジェクトの送信
この例では、sendObjectメソッドを使用して、MessageAオブジェクトおよびMessageBオブジェクトをWebSocketメッセージとして送信します。
import javax.websocket.Session; ... import com.example.game.message.MessageA; import com.example.game.message.MessageB; ... MessageA msgA = new MessageA(...); MessageB msgB = new MessageB(...); session.getBasicRemote.sendObject(msgA); session.getBasicRemote.sendObject(msgB); ...
エンコーダとは異なり、バイナリ・メッセージ用デコーダとテキスト・メッセージ用デコーダは、それぞれ最大で1つしか指定できません。エンドポイントと同様に、デコーダ・インスタンスは1つのWebSocket接続およびピアにのみ関連付けられているので、デコーダ・インスタンスのコードを実行しているスレッドは常に1つのみです。
WebSocketメッセージをJavaオブジェクトとしてデコードする手順:
WebSocketメッセージのタイプに適したインタフェースを実装します。
テキスト・メッセージには、javax.websocket.Decoder.Text<T>を実装します。
バイナリ・メッセージには、javax.websocket.Decoder.Binary<T>を実装します。
これらのインタフェースはwillDecodeメソッドおよびdecodeメソッドを指定します。
エンドポイントでデコーダ実装が使用されるように指定します。
アノテーション付きエンドポイントの場合は、デコーダ実装の名前をServerEndpointアノテーションのdecodersオプション要素に追加します。
プログラム的なエンドポイントの場合は、デコーダ実装の名前のリストをjavax.websocket.server.ServerEndpointConfig.Builderオブジェクトのdecodersメソッドのパラメータとして渡します。
Message receivedイベントを処理するエンドポイントのメソッドで、カスタムJavaタイプがパラメータとして取得されるようにします。
詳細は、「WebSocket接続のライフサイクル・イベントの処理」を参照してください。
指定されたデコーダでデコード可能なメッセージをエンドポイントが受信すると、コンテナはカスタムJavaタイプをパラメータとして取得するメソッドをコールします(このメソッドが存在する場合)。
次の例では、WebSocketテキスト・メッセージをJavaタイプcom.example.game.message.MessageAおよびcom.example.game.message.MessageBとしてデコードする方法を示します。
この例では、Javaタイプcom.example.game.message.MessageAおよびcom.example.game.message.MessageBでcom.example.game.message.Messageクラスが拡張されているものと仮定します。
例18-20 デコーダ・インタフェースの実装
この例では、Decoder.Text<Message>インタフェースを実装します。
エンドポイントで使用できるテキスト・メッセージ用デコーダは1つのみなので、Messageスーパークラスのデコーダを実装することになります。このデコーダを使用して、Messageのサブクラスをデコードします。
package com.example.game.decoder;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import com.example.game.message.Message;
import com.example.game.message.MessageA;
import com.example.game.message.MessageB;
...
public class MessageTextDecoder implements Decoder.Text<Message> {
@Override
public void init(EndpointConfig ec) { }
@Override
public void destroy() { }
@Override
public Message decode(String string) throws DecodeException {
// Read message...
if ( /* message is an A message */ )
return new MessageA(...);
else if ( /* message is a B message */ )
return new MessageB(...);
}
@Override
public boolean willDecode(String string) {
// Determine if the message can be converted into either a
// MessageA object or a MessageB object...
return canDecode;
}
}
例18-21 アノテーション付きWebSocketエンドポイント用のデコーダの定義
この例では、デコーダ・クラスMessageTextDecoder.classを、WebSocketサーバー・エンドポイントEncEndpointに定義します。
完全を期すため、この例には、例18-18で示したエンコーダ・クラスMessageATextEncoder.classおよびMessageBTextEncoder.classの定義も含まれています。
package com.example.game;
import javax.websocket.server.ServerEndpoint;
import com.example.game.encoder.MessageATextEncoder;
import com.example.game.encoder.MessageBTextEncoder;
import com.example.game.decoder.MessageTextDecoder;
...
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class },
decoders = { MessageTextDecoder.class }
)
public class EncEndpoint { ... }
例18-22 JavaオブジェクトとしてエンコードされたWebSocketメッセージの受信
この例では、MessageAオブジェクトおよびMessageBオブジェクトを受信するmessageメソッドを定義します。
import javax.websocket.OnMessage;
import javax.websocket.Session;
...
import com.example.game.message.Message;
import com.example.game.message.MessageA;
import com.example.game.message.MessageB;
...
@OnMessage
public void message(Session session, Message msg) {
if (msg instanceof MessageA) {
// We received a MessageA object...
else if (msg instanceof MessageB) {
// We received a MessageB object...
}
}
ServerEndpointアノテーションを使用すると、レベル1のURIテンプレートを使用して、エンドポイント・デプロイメントURIの一部分をアプリケーション・パラメータとして指定できます。URIテンプレートでは、変数拡張を使用してURIの範囲を示します。URIテンプレートの詳細は、http://tools.ietf.org/html/rfc6570を参照してください。
エンドポイント・デプロイメントURIの一部をアプリケーション・パラメータとして指定する手順:
ServerEndpointアノテーションのvalue要素を、使用するURIテンプレートに設定します。
URIテンプレートで、拡張する各変数をカッコで囲みます。
拡張する各変数を、次のいずれかのイベント・タイプを処理するメソッドのパラメータとして宣言します。
Connection opened
Connection closed
Message received
パラメータの型は、String型、プリミティブ型、またはこれらのボックス・バージョンです。
javax.websocket.server.PathParamアノテーションで、パラメータの宣言にアノテーションを付けます。
PathParamアノテーションのvalue要素を、変数の名前に設定します。
パラメータを取得するメソッドの本文に、変数の拡張ロジックを記述します。
例18-23は、エンドポイント・デプロイメントURIの一部をアプリケーション・パラメータとして指定する方法を示しています。
例18-23 エンドポイント・デプロイメントURIの一部をアプリケーション・パラメータとして指定する方法
この例では、エンドポイント・デプロイメントURIを、変数{room-name}が含まれるURIテンプレートとして指定します。この変数は、openメソッドのroomNameパラメータで拡張され、ユーザーがどのチャット・ルームに参加するかを決定します。
import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/chatrooms/{room-name}")
public class ChatEndpoint {
@OnOpen
public void open(Session session,
EndpointConfig c,
@PathParam("room-name") String roomName) {
// Add the client to the chat room of their choice ...
}
...
}
{room-name}変数を拡張するopenメソッドの本文のコードは、この例には示していません。
ローカルのJava EEサーバーでポート8080を使用し、chatappというWebアプリケーション内にエンドポイントをデプロイしている場合、クライアントは次のURIを使用してエンドポイントに接続できます。
http://localhost:8080/chatapp/chatrooms/currentnews http://localhost:8080/chatapp/chatrooms/music http://localhost:8080/chatapp/chatrooms/cars http://localhost:8080/chatapp/chatrooms/technology
コンテナでは、接続ごとにエンドポイント・クラスのインスタンスが作成されるので、クライアントの状態の情報を格納するためのインスタンス変数を定義して使用することができます。
また、Session.getUserPropertiesメソッドには、ユーザー・プロパティを格納するための変更可能なマップが用意されています。
すべての接続クライアント共通の情報を格納するには、クラス(静的)変数を使用できますが、これらの変数へのスレッドセーフ・アクセスが保証されている必要があります。
例18-24は、クライアントの状態を保持する方法を示しています。
例18-24 クライアントの状態の保持
この例では、受信テキスト・メッセージに対して、各クライアントから受け取った直前のメッセージの内容を返します。
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/delayedecho")
public class DelayedEchoEndpoint {
@OnOpen
public void open(Session session) {
session.getUserProperties().put("previousMsg", " ");
}
@OnMessage
public void message(Session session, String msg) {
String prev = (String) session.getUserProperties()
.get("previousMsg");
session.getUserProperties().put("previousMsg", msg);
try {
session.getBasicRemote().sendText(prev);
} catch (IOException e) { ... }
}
}
Java API for WebSocketでは、コンテナでサーバー・エンドポイント・インタフェースを作成する方法を構成できます。次に関して、エンドポイントを構成するカスタム・ロジックを提供できます。
WebSocket接続のハンドシェイク・リクエストの詳細へのアクセス
Origin HTTPヘッダーのカスタム・チェックの実行
WebSocketハンドシェイク・レスポンスの変更
クライアントから要求されるWebSocketサブプロトコルの選択
エンドポイント・インスタンスのインスタンス化と初期化の制御
サーバー・エンドポイントでサポートされる拡張機能の指定
プログラムを使用してサーバー・エンドポイントを構成する手順:
javax.websocket.server.ServerEndpointConfig.Configuratorクラスを拡張します。
次の表を参照して、カスタム・ロジックが必要な構成操作を実行するメソッドをオーバーライドします。
| 構成操作 | オーバーライドするメソッド |
|---|---|
| WebSocket接続のハンドシェイク・リクエストの詳細へのアクセス | modifyHandshake |
Origin HTTPヘッダーのカスタム・チェックの実行 |
checkOrigin |
| WebSocketハンドシェイク・レスポンスの変更 | modifyHandshake |
| クライアントから要求されるWebSocketサブプロトコルの選択 | getNegotiatedSubprotocol |
| エンドポイント・インスタンスのインスタンス化と初期化の制御 | getEndpointInstance |
| サーバー・エンドポイントでサポートされる拡張機能の指定 | getNegotiatedExtensions |
サーバー・エンドポイント・クラスで、ServerEndpointアノテーションのconfigurator要素をコンフィギュレータ・クラスに設定します。
次の例は、プログラムを使用してサーバー・エンドポイントを構成する方法を示しています。
例18-25 ServerEndpointConfig.Configuratorクラスの拡張
この例では、ServerEndpointConfig.Configuratorクラスを拡張して、ハンドシェイク・リクエスト・オブジェクトをエンドポイント・インスタンスで使用できるようにします。
import javax.websocket.HandshakeResponse;
import javax.websocket.server.ServerEndpointConfig.Configurator;
import javax.websocket.server.HandshakeRequest;
...
public class CustomConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig conf,
HandshakeRequest req,
HandshakeResponse resp) {
conf.getUserProperties().put("handshakereq", req);
}
...
}
例18-26 サーバー・エンドポイント・クラスのカスタム・コンフィギュレータの指定
この例では、カスタム・コンフィギュレータ・クラスCustomConfigurator.classを、サーバー・エンドポイント・クラスMyEndpointに指定します。
カスタム・コンフィギュレータを使用すると、サーバー・エンドポイント・クラスのインスタンスがハンドシェイク・リクエスト・オブジェクトにアクセスできるようになります。サーバー・エンドポイント・クラスは、ハンドシェイク・リクエスト・オブジェクトを使用して、ハンドシェイク・リクエストの詳細(ヘッダーやHttpSessionオブジェクトなど)にアクセスします。
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
...
@ServerEndpoint(
value = "/myendpoint",
configurator = CustomConfigurator.class
)
public class MyEndpoint {
@OnOpen
public void open(Session s, EndpointConfig conf) {
HandshakeRequest req = (HandshakeRequest) conf.getUserProperties()
.get("handshakereq");
Map<String,List<String>> headers = req.getHeaders();
...
}
}
WebLogic Serverでは、wlserver/server/lib/api.jarファイル内にJava API for WebSocketが用意されています。Java API for WebSocketを使用するアプリケーションを構築するには、アプリケーションのコンパイル時にクラスパスにこのライブラリを定義します。
Mavenを使用して、Java API for WebSocketを使用するアプリケーションを構築することもできます。Mavenを使用する場合は、Java API for WebSocketが格納されているMavenアーティファクト(javax.websocket.javax.websocket-api:1.0)をMavenセントラルから取得します。詳細は、「WebLogic Mavenプラグインの使用方法」を参照してください。
WebLogic Serverでは、標準のJava EE Webアプリケーション・アーカイブ(WAR)の一部としてWebSocketアプリケーションをデプロイします。これは、スタンドアロンのWebアプリケーションまたはエンタープライズ・アプリケーション内のWARモジュールのいずれかとして行います。
web.xmlファイルやその他のデプロイメント記述子でWebSocketエンドポイントを構成したり、WebSocketエンドポイントを登録または有効にするために動的な操作を実行する必要はありません。
ただしオプションで、表18-3に表示されているコンテキスト初期化プロパティを設定できます。これらのプロパティがWebLogic Server固有で、JSR 356の仕様に含まれていないことを示すため、それぞれの完全修飾名には接頭辞weblogic.websocketが付いています。
表18-3 WebSocketアプリケーションのコンテキスト初期化プロパティ
| プロパティ | タイプ | 説明 |
|---|---|---|
|
|
|
メッセージ受信用の内部の最大バッファ・サイズ(バイト)。アプリケーションは、このサイズを超えるメッセージを処理できません。 このパラメータは、次のサーバー・セッションおよびクライアント・セッションに影響します。
デフォルトのバッファ・サイズは4194315 (ペイロードに4MB、フレームのオーバーヘッドに11バイト)です。 |
|
|
|
接続のアイドル・タイムアウト後の最大時間(ミリ秒)デフォルト値は30000 (30秒に相当)です。 |
例18-27は、WebSocketアプリケーションにコンテキスト初期化プロパティを設定する方法を示しています。
例18-27 WebSocketアプリケーションのコンテキスト初期化プロパティの設定
この例では、WebSocketアプリケーションにコンテキスト初期化パラメータを次のように設定します。
メッセージ受信用の内部の最大バッファ・サイズを16777227バイトに設定します。
接続のアイドル・タイムアウト後の最大時間を60,000ミリ秒(1分に相当)に設定します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" ...>
...
<context-param>
<param-name>weblogic.websocket.tyrus.incoming-buffer-size</param-name>
<param-value>16777227</param-value>
</context-param>
<context-param>
<param-name>weblogic.websocket.tyrus.session-max-idle-timeout</param-name>
<param-value>60000</param-value>
</context-param>
</web-app>
WebSocketアプリケーションにアクセスするクライアントは、WebLogic Serverインスタンスに直接接続するか、またはWebSocketプロトコルをサポートするWebプロキシ・サーバーを介して接続する必要があります。
次のプロキシ・サーバーはWebSocketプロトコルをサポートします。
Oracle Traffic Director
Oracle HTTP Server (リリース12.1.3以降)
Apache 2.2 or 2.4 (Oracle WebLogicのWebサーバー・プラグイン(12.1.2.0.0)とともに使用される場合)
通常、WebSocketクライアント・アプリケーションはブラウザ・ベースのクライアントです。Java API for WebSocketは、Java WebSocketクライアントの記述にも使用できます。
ブラウザ・ベースのWebSocketクライアント・アプリケーションは、通常、HTML5技術(HTMLマークアップ、CSS3、WebSocket JavaScript APIを利用するJavaScriptなど)の複合体です。HTML5の詳細は、http://www.w3.org/TR/html5/を参照してください。
ほとんどのブラウザでは、WebSocketプロトコルを作成して動かすために使用できるW3C WebSocket APIをサポートしています。W3C WebSocket APIの詳細は、http://www.w3.org/TR/websockets/を参照してください。
実行時環境でWebSocketプロトコルのサポートが保証されていない場合は、ブラウザ・ベースのクライアントでJavaScript API for WebSocketのフォールバックを使用してください。このAPIは、標準のW3C WebSocket API実装を提供します。また、このAPIは、WebSocketプロトコルがサポートされていない場合に、WebSocketメッセージングの代替転送方法を使用するためのメカニズムも提供します。詳細は、「WebSocketメッセージングのプロトコル・フォールバックの有効化」を参照してください。
次の手順は、WebSocketプロトコルを使用してWebLogic Serverインスタンスにメッセージを送信する、クライアント上の実行フローの例を示しています。
クライアントは、ws://またはwss://プロトコル接頭辞を使用して、WebSocketエンドポイントをホストするサーバーへのWebSocket接続を開きます。詳細は、「セキュアなWebSocket接続の確立」を参照してください。
var url = ((window.location.protocol == "https:") ? "wss:" : "ws:") + "//" + window.location.host + "/websocket-helloworld-wls/helloworld_delay.ws"; var ws = new WebSocket(url);
クライアントは、リスナーをWebSocketオブジェクトに登録して、メッセージのオープン、クローズ、受信などのイベントに応答します。受信したイベントや情報に基づき、クライアントは適切なアクションを実行します。
ws.onopen = function(event) {
document.getElementById("status").innerHTML = "OPEN"
}
ws.onmessage = function(event) {
msg = event.data
document.getElementById("short_msg").innerHTML =
event.data;
}
クライアントは、アプリケーションの必要に応じて、WebSocketオブジェクトを介してサーバーにメッセージを送信します。
function sendMsg() {
// Check if connection is open before sending
if(ws == null || ws.readyState != 1) {
document.getElementById("reason").innerHTML
= "Not connected can't send msg"
} else {
ws.send(document.getElementById("name").value);
}
}
<input id="send_button" class="button" type="button" value="send" onclick="sendMsg()"/>
javax.websocketパッケージには、クライアントとサーバーのエンドポイントに共通のアノテーション、クラス、インタフェースおよび例外が含まれています。このパッケージのAPIを使用して、サーバーを記述する場合と同じようにJava WebSocketクライアントを記述します。クライアントの記述に固有の追加のプログラミング・タスクについては、後続の項で説明します。
WebLogic Serverには、コンテナでクライアント・エンドポイント・インタフェースを作成する方法を構成するプロパティが用意されています。これらのプロパティがWebLogic Server固有で、JSR 356の仕様に含まれていないことを示すため、それぞれの完全修飾名には接頭辞weblogic.websocketが付いています。
WebLogic Serverは次のプロパティを提供します。
HTTPプロキシ構成。WebSocketプロトコル(RFC 6455)で定義されているように、WebLogic Serverでは、HTTPプロキシ経由でリモート・サーバーのWebSocketエンドポイントにクライアント接続できます。
HTTPプロキシ構成プロパティを表18-4に示します。
Secure Sockets Layer (SSL)構成。WebLogic Serverでは、wssスキームを使用してSSL経由でリモート・サーバーのWebSocketエンドポイントにクライアント接続できます。
SSL構成プロパティを表18-5に示します。
受信メッセージのバッファ・サイズ。WebLogic Serverでは、WebSocketクライアント・エンドポイントの受信メッセージのサイズを制限できます。
バッファ・サイズ構成プロパティを表18-6に示します。
表18-4 Java WebSocketクライアントのHTTPプロキシ構成プロパティ
| プロパティ | タイプ | 説明 |
|---|---|---|
|
|
|
HTTPプロキシ・ホストの名前。JavaScriptクライアントのプロキシ設定を構成する場合は、このプロパティを指定する必要があります。 |
|
|
|
オプション。HTTPプロキシ・ホストへの接続に使用するポート番号。ポート番号なしでHTTPプロキシ・ホストを指定する場合、ポート番号はデフォルトの80になります。 |
|
|
|
オプション。プロキシ・ホストにログインするためのユーザー名。 |
|
|
|
オプション。プロキシ・ホストにログインするためのユーザー名。 |
表18-5 Java WebSocketクライアントのSSL構成プロパティ
| プロパティ | タイプ | 説明 |
|---|---|---|
|
|
|
オプション。サポートされているSSLプロトコル・バージョンのカンマ区切りリスト。 |
|
|
|
オプション。キーストア・ファイル(SSL暗号化で使用するセキュリティ証明書を格納)へのパス。 |
|
|
|
オプション。キーストアのパスワード。 |
表18-6 Java WebSocketクライアントのバッファ・サイズ構成プロパティ
| プロパティ | タイプ | 説明 |
|---|---|---|
|
|
|
メッセージ受信用の内部の最大バッファ・サイズ(バイト)。クライアントは、このサイズを超えるメッセージを処理できません。 このプロパティが設定されている場合は、表18-3に表示された同じ名前のコンテキスト初期化プロパティの値がオーバーライドされます。 デフォルトのバッファ・サイズは4194315 (ペイロードに4MB、フレームのオーバーヘッドに11バイト)です。 |
|
注意: クライアント・エンドポイントは、クライアントをサーバー・エンドポイントに接続する前に構成してください。 |
プログラムを使用してWebSocketクライアント・エンドポイントを構成する手順:
javax.websocket.ClientEndpointConfigオブジェクトを取得します。
javax.websocket.ClientEndpointConfig.Builder.create静的メソッドを呼び出して、ClientEndpointConfig.Builderクラスのインスタンスを取得します。
前の手順で取得したClientEndpointConfig.Builderオブジェクト上で、buildメソッドを呼び出します。
変更する各構成プロパティを新しい値に設定します。
前の手順で取得したClientEndpointConfigオブジェクト上でgetUserPropertiesメソッドを呼び出し、ユーザー・プロパティが格納された変更可能なjava.util.Mapオブジェクトを取得します。
前の手順で取得したMapオブジェクト上で、putメソッドを呼び出します。
putメソッドの呼出しでは、プロパティ名とその新しい値をパラメータとしてメソッドに提供します。
例18-28は、プログラムを使用してWebSocketクライアント・エンドポイントを構成する方法を示しています。
例18-28 WebSocketクライアント・エンドポイントのプログラムによる構成
この例では、プログラムを使用してWebSocketクライアント・エンドポイントを次のように構成します。
HTTPプロキシ・ホスト名をproxy.example.comに設定します。
HTTPプロキシ・ホストへの接続に使用するポート番号を80に設定します。
キーストア・ファイルへのパスを/export/keystoreに設定します。
キーストアのパスワードをchangeitに設定します。
メッセージ受信用の内部の最大バッファ・サイズを16777227バイト(ペイロードに16MB、フレームのオーバーヘッドに11バイト)に設定します。
...
import javax.websocket.ClientEndpointConfig;
...
ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
// configure the proxy host
cec.getUserProperties().put("weblogic.websocket.client.PROXY_HOST",
"proxy.example.com");
// configure the proxy port
cec.getUserProperties().put("weblogic.websocket.client.PROXY_PORT", 80);
// configure the trust keystore path
cec.getUserProperties().put("weblogic.websocket.client.SSL_TRUSTSTORE",
"/export/keystore");
// configure the trust keystore's password
cec.getUserProperties().put("weblogic.websocket.client.SSL_TRUSTSTORE_PWD",
"changeit");
// for receiving 16 Mbyte payload
cec.getUserProperties().put("weblogic.websocket.tyrus.incoming-buffer-size",
16 * 1024 * 1024 + 11);
...
Java WebSocketクライアントをサーバー・エンドポイントに接続する手順:
javax.websocket.ContainerProvider.getWebSocketContainer()静的メソッドを呼び出して、クライアントのjavax.websocket.WebSocketContainerインスタンスを取得します。
前の手順で取得したWebSocketContainerオブジェクト上で、オーバーロードしたconnectToServerメソッドを呼び出します。
メソッドを呼び出す際の変数は、エンドポイントがアノテーション付きエンドポイントかプログラム的なエンドポイントかによって、またJava EEサービス(依存性の注入など)のサポートが必要かによって異なります。
| エンドポイントのタイプ | Java EEサービスのサポート | connectToServerメソッドの変数 |
|---|---|---|
| アノテーション付き | オプション |
connectToServer(Object annotatedEndpointInstance, URI path) |
| アノテーション付き | 必須 |
connectToServer(Class<?> annotatedEndpointClass, URI path) |
| プログラム的 | オプション |
connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) |
| プログラム的 | 必須 |
connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig cec, URI path) |
connectToServerメソッドの呼出しでは、次の情報をパラメータとしてメソッドに提供します。
クライアントのWebSocketエンドポイント
サーバーのWebSocketエンドポイントへの完全パス
クライアント・エンドポイントがプログラム的なエンドポイントの場合は、エンドポイントの構成情報も提供する必要があります。
例18-29は、Java WebSocketクライアントをサーバー・エンドポイントに接続する方法を示しています。
例18-29 Java WebSocketクライアントのサーバー・エンドポイントへの接続
この例では、Java WebSocketクライアントClientExampleをWebSocketサーバー・エンドポイント(ws://example.com:80/echoserver/echo)に接続します。WebSocketクライアント・エンドポイントはクラスExampleEndpointで表現されます。ExampleEndpointクラスの宣言を例18-4に示します。
import java.io.IOException;
import java.net.URI;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
...
public class ClientExample {
public static void main(String[] args) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(ExampleEndpoint.class,
new URI("ws://example.com:80/echoserver/echo"));
...
session.close();
}
デフォルトで、WebSocketクライアントからメッセージをディスパッチするための最大スレッド数は、使用可能なプロセッサの数によって異なります。
使用可能なプロセッサ数が20以下の場合、最大スレッド数は20です。
使用可能なプロセッサ数が20を超える場合、最大スレッド数は使用可能なプロセッサ数と同数です。
WebSocketクライアントからメッセージをディスパッチするための最大スレッド数を設定する手順:
クライアント・アプリケーションを起動するjavaコマンドで、システム・プロパティweblogic.websocket.client.max-aio-threadsを必要な数に設定します。
例18-30は、WebSocketクライアントからメッセージをディスパッチするための最大スレッド数を設定する方法を示しています。
WebLogic Serverでは、Webアプリケーション・アーカイブ(WAR)の一部としてWebSocketアプリケーションをデプロイします。これは、スタンドアロンのWebアプリケーションまたはエンタープライズ・アプリケーション内のWARモジュールのいずれかとして行います。したがって、Webアプリケーションの保護に適用する多くのセキュリティ上の措置を、WebSocketアプリケーションに適用することができます。Webアプリケーションのセキュリティの詳細は、『WebLogicセキュリティ・サービスによるアプリケーションの開発』のセキュアなWebアプリケーションの開発に関する項を参照してください。
次の項では、WebLogic ServerにおけるWebSocketアプリケーションのセキュリティに関する考慮事項について説明します。
最新のブラウザでは、ある生成元からロードされたWebページ上で実行されているスクリプトが、別の生成元のリソースとやりとりしないようにするために、同一生成元ポリシーが使用されています。WebSocketプロトコル(RFC 6455)では、サーバーが生成元横断接続に同意するかどうかを決定できる、検証済の生成元ポリシーが使用されています。
スクリプトがWebSocketアプリケーションにオープニング・ハンドシェイク・リクエストを送信すると、WebSocketハンドシェイク・リクエストを使用してOrigin HTTPヘッダーが送信されます。アプリケーションは、Originを検証しない場合、すべての生成元からの接続を受けれ入れます。予想される生成元以外の生成元からの接続を受け入れないようにアプリケーションを設定すると、WebSocketアプリケーションは、接続を拒否することができます。
javax.websocket.server.ServerEndpointConfig.Configuratorクラスを拡張することにより、WebSocketアプリケーションでOriginが確実に検証されるようにできます。
次のコード・サンプルは、検証済の生成元ポリシーを適用する方法を示しています。
...
import javax.websocket.server.ServerEndpointConfig;
public class MyConfigurator extends ServerEndpointConfig.Configurator {
...
private static final String ORIGIN = "http://www.example.com:7001";
@Override
public boolean checkOrigin(String originHeaderValue) {
return ORIGIN.equals(originHeaderValue)
}
}
...
詳細は、「サーバー・エンドポイントのプログラムによる構成」を参照してください。
|
注意: ブラウザ以外のクライアント(Javaクライアントなど)では、WebSocketハンドシェイク・リクエストを使用してOrigin HTTPヘッダーを送信する必要はありません。WebSocketハンドシェイク・リクエストにOriginヘッダーが含まれない場合、リクエストはブラウザ以外のクライアントからのものであり、ハンドシェイク・リクエストにOriginヘッダーが含まれる場合、ブラウザから、またはブラウザ以外のクライアントからの場合があります。
ブラウザ以外のクライアントが、 |
WebSocketプロトコル(RFC 6455)では、ハンドシェイク処理時のWebSocketクライアントの認証方法を指定しません。標準のWebコンテナ認証および認可機能を使用すると、未認可のクライアントがサーバー上でWebSocket接続を開かないようにすることができます。
Webアプリケーションでサポートされている<auth-method要素の構成はすべて、WebSocketアプリケーションにも使用できます。これらの認証タイプとして、BASIC、FORM、CLIENT-CERTなどがあげられます。詳細は、『WebLogicセキュリティ・サービスによるアプリケーションの開発』のセキュアなWebアプリケーションの開発に関する項を参照してください。
WebSocketアプリケーションのweb.xmlデプロイメント記述子ファイルで該当する<security-constraint要素を構成することにより、アプリケーション内のエンドポイントへのパスをセキュリティで保護することができます。クライアントは、<security-constraintを構成することで、WebSocketハンドシェイク・リクエストを送信する前に認証する必要があります。そうしない場合、サーバーはWebSocketハンドシェイク・リクエストを拒否します。<security-constraint要素の詳細は、『Oracle WebLogic Server Webアプリケーション、サーブレット、JSPの開発』のweb.xmlデプロイメント記述子要素に関する項を参照してください。
次のコード・サンプルは、アプリケーション内のエンドポイントへのパスをセキュリティで保護する方法を示しています(ここで、パスは/demoです)。
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured WebSocket Endpoint</web-resource-name>
<url-pattern>/demo</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>user</role-name>
</security-role>
BASICおよびDIGEST認証方式を実装し、javax.websocket.ClientEndpointConfig.Configuratorクラスを使用してハンドシェイクのメッセージ・ヘッダーを操作することにより特定のクライアントを許可するように、WebSocketアプリケーションを構成できます。クライアントがWebSocket接続を作成することをアプリケーションが認可しない場合、サーバーはそのクライアントからのWebSocketハンドシェイク・リクエストを拒否します。
ハンドシェイクのオープン時にクライアントから渡されたOriginヘッダーの値をチェックするには、javax.websocket.server.ServerEndpointConfig.ConfiguratorクラスのcheckOriginメソッドを使用します。カスタム・チェックを使用する場合は、このメソッドをオーバーライドできます。詳細は、「サーバー・エンドポイントのプログラムによる構成」を参照してください。
WebSocket接続を確立するために、クライアントはハンドシェイク・リクエストをサーバーに送信します。wsスキームを使用してWebSocket接続を開く場合、ハンドシェイク・リクエストはプレーンHTTPリクエストになります。確立されたWebSocket接続を介して転送されるデータは暗号化されません。
セキュアなWebSocket接続を確立し、データがインターセプトされないようにするには、WebSocketアプリケーションが、wssスキームを使用する必要があります。wssスキームを使用すると、クライアントがハンドシェイク・リクエストをHTTPSリクエストとして送信し、転送データがTLS/SSLで暗号化されます。
HTTPSハンドシェイク・リクエストのみを受け入れるようにWebSocketアプリケーションを構成することができます。この場合、すべてのWebSocket接続を暗号化する必要があり、暗号化されていないWebSocketハンドシェイク・リクエストは拒否されます。WebSocketアプリケーションのweb.xmlデプロイメント記述子ファイルで<user-data-constraint要素を指定します。<user-data-constraint要素の詳細は、『Oracle WebLogic Server Webアプリケーション、サーブレット、JSPの開発』のweb.xmlデプロイメント記述子要素に関する項を参照してください。
次のコード・サンプルは、<user-data-constraint>要素の構成方法を示しています。
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured WebSocket Endpoint</web-resource-name>
<url-pattern>/demo</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
スクリプトではws:// URI (プレーンHTTPリクエストを使用)を通してWebSocket接続を開こうとし、一方でトップレベルのWebページがHTTPSリクエストを通して取得される場合、そのWebページは複合コンテンツと呼ばれます。ほとんどのブラウザは複合コンテンツを許可しなくなっていますが、まだ許可しているものもあります。WebSocketアプリケーションでは混合コンテンツを回避する必要があります。なぜなら、これによってJSESSIONIDやCookieなどの保護する必要がある特定の情報の公開が可能になるからです。
混合コンテンツの詳細は、http://www.w3.org/TR/wsc-ui/#securepageのWeb Security Context: User Interface Guidelinesを参照してください。
WebSocket接続に制限を指定すると、クライアントによってサーバーのリソース(メモリーやソケットなど)が使い果されてしまうことを回避できます。
WebSocket接続には次の制限を指定できます。
最大メッセージ・サイズ。WebSocket接続に最大メッセージ・サイズを設定するには、onMessageアノテーションのmaxMessageSize要素をバイト単位のサイズに設定します。
アイドル・タイムアウト値。WebSocket接続にアイドル・タイムアウト値を設定するには、次のいずれかのメソッドを呼び出します。
個別の接続には、SessionオブジェクトのsetMaxIdleTimeoutメソッドを呼び出します。
コンテナ全体では、WebSocketContainerオブジェクトのsetDefaultMaxSessionIdleTimeoutメソッドを呼び出します。
プロトコル・フォールバックは、WebSocketプロトコルがサポートされていない場合に、WebSocketメッセージングの代替転送方法を使用するためのメカニズムを提供します。通常、WebSocketプロトコルがサポートされない理由は、WebSocketオブジェクトが使用できないためか、WebSocketフレームがファイアウォールによってブロックされているためです。このリリースでサポートされている代替転送方法は、HTTPロング・ポーリングのみです。
プロトコル・フォールバックを使用すると、実行時環境でWebSocketプロトコルがサポートされているかどうかにかかわらず、標準のプログラミングAPIを使用してWebSocketメッセージングを実行できます。
|
注意: WebSocketフォールバックをサポートするには、サーバーで固有のWebLogic Server WebSocket APIではなく、JSR 356 Java API for WebSocketを使用する必要があります。 |
JavaScript API for WebSocketのフォールバックは、標準のW3C WebSocket APIと追加のAPIの実装を提供するので、WebSocketフォールバックが簡単に行えます。JavaScript API for WebSocketのフォールバックの詳細は、『JavaScript API Reference for WebSocket Fallback』を参照してください。W3C WebSocket APIの詳細は、http://www.w3.org/TR/websockets/を参照してください。
標準のW3C WebSocket JavaScript APIを使用する場合は、WebSocketプロトコルがサポートされているかどうか気にせずにアプリケーションのコードを記述できます。
WebLogic Serverには、WebSocketフォールバックを構成するプロパティが用意されています(表18-7を参照)。
表18-7 WebSocketフォールバックの構成プロパティ
| プロパティ | タイプ | デフォルト | 説明 |
|---|---|---|---|
|
|
string |
. |
|
|
|
integer |
0 |
デバッグ・レベル。 |
|
|
integer |
10 |
Internet Explorerブラウザのバージョンを示し、これよりも古い場合、フレーム・データにはBase16エンコーディングを使用します。 |
|
|
ブール |
いいえ |
Base16エンコーディングを使用するかどうかを示します。 |
|
|
integer |
2 |
特定の転送方法で接続を確立する最大連続再試行回数。 |
|
|
integer |
25000 |
サーバーに送信する連続ping間の時間間隔(ミリ秒)。 |
|
|
ブール |
true |
クライアントからサーバーへのping送信が有効かどうか。 |
|
|
string |
なし |
実行する転送方法(次のいずれかの転送方法):
|
|
|
integer |
1000 |
失敗した接続試行を同じ転送方法で再試行するまでの時間間隔(秒)。転送の再試行回数は増加しません。 この時間(ミリ秒)内での接続に失敗すると、再試行回数が1ずつ増加します。 |
|
|
integer |
1000 |
WebSocket接続の作成が失敗したと判断されるまでの時間(ミリ秒)。 |
WebSocketプロトコルを使用できる場合は、プロトコル・フォールバックが有効であっても、WebLogic Serverではこのプロトコルが使用されます。WebLogtic Serverでは、接続試行が失敗すると、TRY_AGAIN_INTERVALプロパティおよびNB_TRY_FOR_EACH_TRANSPORTプロパティの値を次のように使用して、WebSocketプロトコルが使用可能かどうかを判断します。
TRY_AGAIN_INTERVALの時間(ミリ秒)内に接続が確立されない場合は、同じ転送方法で再試行されます。この転送の再試行回数は増加しません。
TRY_AGAIN_INTERVALの時間(ミリ秒)内での接続に失敗すると、再試行回数が1ずつ増加します。
再試行回数がNB_TRY_FOR_EACH_TRANSPORTの値に達すると、次の転送方法が試行されます。
最後の転送方法の再試行回数がNB_TRY_FOR_EACH_TRANSPORTの値に達すると、接続が閉じられます(つまり、クライアント上でonclose関数がコールされます)。
WebSocketフォールバックの構成手順:
必要な構成プロパティを設定するJSONオブジェクトを作成します。
これらのプロパティの詳細は、表18-7を参照してください。
オブジェクトをパラメータとして次のいずれかの関数に渡します。
フォールバック・メカニズムがあるかどうか保証できない場合は、WebSocketオブジェクトを作成する前に、オブジェクトをパラメータとしてOraSocket.configure関数に渡します。
WebSocketフォールバックのJavaScriptライブラリが使用できない場合に、アプリケーションが失敗しないようにするには、try/catchブロック内でOraSocket.configure関数をコールします。
あるいは、オブジェクトをWebSocketオブジェクトのコンストラクタの第2パラメータ(オプション)として渡します。
例18-31は、WebSocketフォールバックの構成方法を示しています。
例18-31 WebSocketフォールバックの構成
この例では、XMLHttpRequest転送を実行して、デバッグ・レベルを10に設定し、クライアントからサーバーへのping送信を無効化します。
...
try {
var config = {};
config = { transport: XMLHttpRequest, debug: 10, SERVER_PING_ENABLED: false };
OraSocket.config(config);
} catch (err) {
console.log("Error creating WebSocket:" + JSON.stringify(err));
}
...
WebSocketオブジェクトは、クライアントからリモート・ホストへのWebSocket接続を表します。
WebSocketオブジェクトを作成するには、WebSocketコンストラクタを呼び出して、次の情報をパラメータとして渡します。
クライアントの接続先URL
(オプション) WebSocketフォールバックの構成設定が格納されているJSONオブジェクト
JSONオブジェクトの詳細は、「WebSocketフォールバックの構成」を参照してください。
例18-32は、WebSocketオブジェクトの作成方法を示しています。
表18-32 WebSocketオブジェクトの作成
この例では、WebSocketオブジェクトwsを作成します。例では、JavaScriptの標準関数を使用して、このコードが含まれているドキュメントのURLから、クライアントが接続するURLを判別します。
...
var URI_SUFFIX = "/websocket-101/ws-101-app";
var ws;
var connectionStatus = "Connecting...";
var calledBy = document.location.toString();
var machine, port, secured;
var regExp = new RegExp("(http|ws)(.?):[/]{2}([^/|^:]*):?(\\d*)/(.*)");
var matches = regExp.exec(calledBy);
secured = matches[2];
machine = matches[3];
port = matches[4];
...
statusFld = document.getElementById('status');
...
try
{
var wsURI = "ws" + secured + "://" + machine + ":" + port + URI_SUFFIX;
ws = new WebSocket(wsURI);
}
catch (err)
{
var mess = 'WebSocket creation error:' + JSON.stringify(err);
connectionStatus = "<font color='red'>Unable to connect.</font>";
if (statusFld !== undefined)
statusFld.innerHTML = mess;
else
alert(mess);
}
...
JavaScript WebSocketクライアントのライフサイクル・イベントの処理では、WebSocketオブジェクトのコールバック関数(表18-8を参照)を記述します。この表には、イベント・タイプ別の処理方法を示した例への相互参照も提供されています。
表18-8 ライフサイクル・イベントを処理するコールバック関数
| イベント | コールバック関数 | 例 |
|---|---|---|
|
Connection opened |
|
|
|
Message received |
|
|
|
Error |
|
|
|
Connection closed |
|
|
例18-33 JavaScript WebSocketクライアントのConnection openedイベントの処理
この例では、JavaScriptの標準関数を使用して、接続が開いたときに現在の日付と時刻、メッセージConnection openedを順に表示します。
...
ws.onopen = function()
{
try
{
var text;
try
{
text = 'Message:';
}
catch (err)
{
text = '<small>Connected</small>';
}
promptFld.innerHTML = text;
if (nbMessReceived === 0)
statusFld.innerHTML = "";
statusFld.innerHTML += ((nbMessReceived === 0?"":"<br>") + "<small>" +
(new Date()).format("d-M-Y H:i:s._ Z") +
"</small>:<font color='blue'>" +
' Connection opened.' + "</font>");
statusFld.scrollTop = statusFld.scrollHeight;
nbMessReceived++;
}
catch (err) {}
};
...
例18-34 JavaScript WebSocketクライアントのMessage receivedイベントの処理
この例では、JavaScriptの標準関数を使用して、メッセージを受信したときに現在の日付と時刻、メッセージの内容を順に表示します。
...
ws.onmessage = function(message) // message/event
{
var json = {};
if (typeof(message.data) === 'string')
{
try
{
json = JSON.parse(message.data);
}
catch (e)
{
console.log(e);
console.log('This doesn\'t look like valid JSON: ' + message.data);
}
}
if (json.type !== undefined && json.type === 'message' &&
typeof(json.appdata.text) === 'string') // it's a single message, text
{
var dt = new Date();
/**
* Add message to the chat window
*/
var existing = contentFld.innerHTML; // Content already there
var toDisplay = "";
try { toDisplay = json.appdata.text; }
catch (err) {}
contentFld.innerHTML = existing +
('At ' +
+ (dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':'
+ (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes())
+ ': ' + toDisplay + '<br>');
contentFld.scrollTop = contentFld.scrollHeight;
}
else // Unexpected
{
var payload = {};
}
};
...
例18-35 JavaScript WebSocketクライアントのErrorイベントの処理
この例では、JavaScriptの標準関数を使用して、エラー発生時に現在の日付と時刻、エラー・メッセージを順に表示します。
...
ws.onerror = function(error)
{
if (nbMessReceived === 0)
statusFld.innerHTML = "";
statusFld.innerHTML += ((nbMessReceived === 0?"":"<br>") + "<small>" +
(new Date()).format("d-M-Y H:i:s._ Z") +
"</small>:<font color='red'>" + error.err + "</font>");
statusFld.scrollTop = statusFld.scrollHeight;
nbMessReceived++;
};
...
例18-36 JavaScript WebSocketクライアントのConnection closedイベントの処理
この例では、JavaScriptの標準関数を使用して、接続が閉じたられたときに現在の日付と時刻、メッセージConnection closedを順に表示します。
...
ws.onclose = function()
{
if (nbMessReceived === 0)
statusFld.innerHTML = "";
statusFld.innerHTML += ((nbMessReceived === 0?"":"<br>") + "<small>" +
(new Date()).format("d-M-Y H:i:s._ Z") +
"</small>:<font color='blue'>" + ' Connection closed' +
"</font>");
promptFld.innerHTML = 'Connection closed';
};
...
JavaScript WebSocketクライアントからメッセージを送信する手順:
メッセージ送信用の関数を定義します。
メッセージ送信用の関数の本文内で、WebSocketオブジェクトのsend関数をコールします。
メッセージ送信用に定義した関数をコールします。
次の例では、JavaScript WebSocketクライアントからメッセージを送信する方法を示します。
例18-37 メッセージ送信用の関数の定義
この例では、メッセージ送信用の関数sendを定義します。
この例で使用するws WebSocketオブジェクトの作成方法は、例18-32を参照してください。
...
var send = function(mess)
{
ws.send(mess);
};
...
例18-38 メッセージ送信用の関数のコール
この例では、ユーザーが「Send」をクリックするとsend関数がコールされ、テキスト・フィールドの内容が送信されます。
send関数の定義方法は、例18-37を参照してください。
...
<input type="text" id="input" style="border-radius:2px; border:1px solid #ccc;
margin-top:10px; padding:5px; width:400px;"
placeholder="Type your message here"/>
<button onclick="javascript:send(document.getElementById('input').value);">Send</button>
...
Webアプリケーションのscriptsディレクトリにorasocket.min.jsファイルをパッケージ化します。
クライアント・アプリケーションに次のscript要素を追加して、orasocket.min.jsの場所を指定します。
<script type="text/javascript" src="scripts/orasocket.min.js"></script>
デフォルトで、WebSocketフォールバックは無効になっています。
WebSocketフォールバックを有効化するには、アプリケーションのデプロイメント記述子ファイルweb.xmlでcom.oracle.tyrus.fallback.enabledコンテキスト・パラメータをtrueに設定します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" ...>
...
<context-param>
<description>Enable fallback mechanism</description>
<param-name>com.oracle.tyrus.fallback.enabled</param-name>
<param-value>true</param-value>
</context-param>
</web-app>
WebLogic Server 12.1.3以降、パッケージweblogic.websocketおよびweblogic.websocket.annotationは非推奨となり、将来のリリースでは削除される予定です。これらのパッケージが削除された後は、WebSocketプロトコル経由の接続にこれらのパッケージを使用できなくなります。
使用中のWebSocketアプリケーションとWebLogic Serverの将来のリリースとの互換性を確保するには、非推奨になったパッケージのかわりにJSR 356 Java API for WebSocketを使用してください。
表18-9は、WebSocketアプリケーション開発タスクの実行で使用する固有のWebLogic Server WebSocket APIと、それに対応するJSR 356 APIを示しています。この表には、アノテーション付きエンドポイント用のJSR 356 APIのみが表示されています。また、タスク別に、JSR 356 APIを使用してタスクを実行するための操作手順への相互参照も提供されています。
表18-9 JSR 356 APIと固有のWebLogic Server WebSocket APIとの比較
| タスク | 固有のWebLogic Server WebSocket API | JSR 356 API | 操作手順 |
|---|---|---|---|
|
サーバー・エンドポイント・クラスの作成 |
|
|
|
|
Connection openedイベントの処理 |
WebSocketListener |
イベントを処理するメソッドの |
|
|
Message receivedイベントの処理 |
|
イベントを処理するメソッドの |
|
|
Errorイベントの処理 |
WebSocketListener |
イベントを処理するメソッドの |
|
|
Connection closedイベントの処理 |
WebSocketListener |
イベントを処理するメソッドの |
|
|
メッセージの送信 |
|
|
|
|
エンドポイントに接続しているすべてのピアへのメッセージ送信 |
|
Session |
|
|
WebSocket接続の最大メッセージ・サイズの設定 |
WebSocket |
onMessage |
|
|
WebSocket接続のアイドル・タイムアウト値の設定 |
WebSocket |
次のうちいずれか:
|
|
|
WebSocket接続の最大オープン接続数の設定 |
WebSocket |
JSR 356 Java API for Websocketではサポート対象外 |
固有のWebSocketサーバー・エンドポイントで使用するAPIをJSR 356 APIに変換する手順:
WebSocketクラスをアノテーション付きサーバー・エンドポイント・クラスに変換します。
WebSocketクラスからアノテーション付きエンドポイント・クラスへの変換は、WebSocketクラスからプログラム的なエンドポイント・クラスへの変換よりも、変更が少なくて済みます。
クラス宣言からextends WebSocketAdapter句またはimplements WebSocketListener句を削除して、WebSocketクラスをPOJOクラスに変換します。
クラス宣言のweblogic.websocket.annotation.WebSocketアノテーションをjavax.websocket.server.ServerEndpointアノテーションで置き換えます。
詳細は、「アノテーション付きエンドポイントの作成」を参照してください。
|
注意: 既存のエンドポイントのpathPatterns要素に/*接尾辞が含まれている場合、/*接尾辞と同じ結果が得られるようにコードを書き換える必要があります。詳細は、「パスのパターン文字列の/*接尾辞の置換え」を参照してください。 |
ライフサイクル・イベントを処理する各メソッドの宣言に、そのメソッドの処理対象であるイベントを指定するアノテーションを付けます。
詳細は、「アノテーション付きWebSocketエンドポイントでのライフサイクル・イベントの処理」を参照してください。
weblogic.websocket.WebSocketConnectionインタフェースへの各参照を、javax.websocket.Sessionインタフェースへの参照で置き換えます。
WebSocketConnectionオブジェクト上の各メソッド呼出しを、Sessionオブジェクト上の対応するメソッドの呼出しで置き換えます。
たとえば、WebSocketConnectionオブジェクトのcloseメソッドは、weblogic.websocket.ClosingMessageオブジェクトをパラメータとして取得します。Sessionオブジェクトのcloseメソッドで、これに対応するパラメータはjavax.websocket.CloseReasonオブジェクトです。
メッセージを送信するためには、Sessionオブジェクト上の各メソッドの呼出しを次のように変更します。
getBasicRemoteメソッドまたはgetAsyncRemoteメソッドの呼出しを追加して、このエンドポイントのピアを表すオブジェクトへの参照を取得します。
非推奨になったAPIのメッセージ送信用のメソッドを、JSR 356 API.の対応するメソッドで置き換えます。
JSR 356 APIのメソッドは、前の手順で参照を取得したjavax.websocket.RemoteEndpoint.Basicオブジェクトまたはjavax.websocket.RemoteEndpoint.Asyncオブジェクトのメソッドです。
詳細は、「メッセージの送信」を参照してください。
| 非推奨のAPIのメソッド | RemoteEndpoint.Basicのメソッド | RemoteEndpoint.Asyncのメソッド |
|---|---|---|
send(String message) |
sendText(String text) |
次のいずれかのメソッド:
sendText(String text) sendText(String text, SendHandler handler) |
send(byte[] message) |
sendBinary(ByteBuffer data) |
次のいずれかのメソッド:
sendBinary(ByteBuffer data) sendBinary(ByteBuffer data, SendHandler handler) |
sendPing(byte[] message) |
sendPing(ByteBuffer applicationData) |
sendPing(ByteBuffer applicationData) |
sendPong(byte[] message) |
sendPong(ByteBuffer applicationData) |
sendPong(ByteBuffer applicationData) |
stream(boolean last, String fragment) |
sendText(String partialMessage, boolean isLast) |
対応するメソッドはありません。 |
stream(boolean last, byte[] fragment, int off, int length) |
sendBinary(ByteBuffer partialByte, boolean isLast) |
対応するメソッドはありません。 |
import句で、非推奨になったAPIのクラスに対する参照を、エンドポイントで使用しているJSR 356 APIのクラスに対する参照で置き換えます。
サーバー・エンドポイントを使用するアプリケーションを再コンパイルして再デプロイします。
非推奨になったAPIのWebSocketアノテーションのpathPatterns要素では、パスのパターン文字列で/*接尾辞を使用できます。/*接尾辞を使用すると、/*接尾辞の前に記述されたパス・パターンで始まるすべてのリソース・パスとパス・パターンが照合されます。たとえば、リソース・パス/ws/chatは、パス・パターン/ws/*に一致します。
JSR 356 APIには、/*接尾辞に相当する機能はありません。既存のエンドポイントで/*接尾辞が使用されている場合、/*接尾辞と同じ結果が得られるようにコードを書き換える必要があります。コードの書換え方法は、/*接尾辞がエンドポイントURIの変数パス・パラメータを表しているのか、エンドポイントの追加データを表しているのかによって異なります。
パスのパターン文字列の/*接尾辞は、エンドポイントURIの1つ以上の変数パス・パラメータを表している場合があります。このような場合は、/*接尾辞のかわりにURIテンプレートを使用します。
JSR 356 APIではレベル1のURIテンプレートのみをサポートし、そこではパス・パラメータはスラッシュ(/)で明確に区切られます。したがって、URIテンプレートでは、既存のエンドポイントの/*接尾辞にかわる変数パス・パラメータごとに1つの拡張変数を定義する必要があります。
たとえば、既存のエンドポイントの/*接尾辞を1つの変数パス・パラメータで置き換える場合は、次の例で示すようにURIテンプレートを定義します。
/ws/{param1}
URI /ws/testは、前の例のテンプレートに一致します。param1変数はtestに拡張されます。
同様に、既存のエンドポイントの/*接尾辞を2つの変数パス・パラメータで置き換える場合は、次の例で示すようにURIテンプレートを定義します。
/ws/{param1}/{param2}
URI /ws/test/chatは、前の例のテンプレートに一致します。param1変数はtestに、param2変数はchatにそれぞれ拡張されます。
詳細は、「エンドポイント・デプロイメントURIの一部をアプリケーション・パラメータとして指定する方法」を参照してください。
パスのパターン文字列の/*接尾辞は、URIの一部として転送される、エンドポイントの追加情報を表している場合があります。このような場合は、/*接尾辞のかわりに問合せパラメータを使用します。
JSR 356の仕様では、問合せパラメータの使用が禁止されたり制限されることはありません。したがって、次の条件を満たせば、問合せパラメータを使用して任意のデータを転送できます。
URLの長さが最大許容長より短いこと。
すべてのデータが適切にエンコードされていること。
エンドポイントの問合せパラメータを取得するには、必要な形式でパラメータを取得できる、エンドポイントのSessionオブジェクトのメソッドを呼び出します。
問合せ全体が含まれている単一の文字列としてパラメータを取得するには、getQueryStringメソッドを呼び出します。例18-39を参照してください。
問合せパラメータのリストが含まれているマップとしてパラメータを取得するには、getRequestParameterMapメソッドを呼び出します。例18-40を参照してください。
例18-39 問合せパラメータを単一の文字列として取得する方法
この例では、リクエストURI /echo?foo=bar,baz,mane,padme,humの問合せパラメータをアプリケーション出力"# foo=bar,baz,mane,padme,hum"として取得します。
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnOpen
public void onOpen(Session session) throws IOException {
System.out.println("# " + session.getQueryString());
}
// ...
}
例18-40 問合せパラメータをマップとして取得する方法
この例では、リクエストURI /echo?foo=bar&foo=baz&foo=mane&foo=padme&foo=humの問合せパラメータをList<String> # [bar, baz, mane, padme, hum]として取得します。
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnOpen
public void onOpen(Session session) throws IOException {
System.out.println("# " + session.getRequestParameterMap().get("foo"));
}
// ...
}
例18-41は、固有のWebSocketサーバー・エンドポイントで使用するAPIを、非推奨になったAPIからJSR 356 APIに変換する方法を示しています。
例18-41 WebSocketサーバー・エンドポイントで使用するAPIをJSR 356 APIに変換する方法
この例では、WebSocketサーバー・エンドポイントで使用するAPIを、非推奨になったAPIからJSR 356 APIに変換する場合に必要な変更を示します。
この例では、非推奨になったコードの行は//コメント文字を使用してコメント・アウトされています。JSR 356 APIのコード行は、コメント//JSR 356で示しています。
package examples.webapp.html5.websocket;
//import weblogic.websocket.ClosingMessage; Deprecated
//import weblogic.websocket.WebSocketAdapter; Deprecated
//import weblogic.websocket.WebSocketConnection; Deprecated
//import weblogic.websocket.annotation.WebSocket; Deprecated
import javax.websocket.CloseReason; //JSR 356
import javax.websocket.OnMessage; //JSR 356
import javax.websocket.Session; //JSR 356
import javax.websocket.server.ServerEndpoint; //JSR 356
import java.io.IOException;
//@WebSocket( Deprecated
// timeout = -1, Deprecated
// pathPatterns = {"/ws"} Deprecated
//)
@ServerEndpoint("/ws") //JSR 356
//public class MessageListener extends WebSocketAdapter { Deprecated
public class MessageListener {
//@Override Not required. Replaced by @OnMessage in a POJO class
@OnMessage //JSR 356
//public void onMessage(WebSocketConnection connection, String payload) { Deprecated
public void onMessage(Session connection, String payload) //JSR 356
throws IOException { //JSR 356
// Sends message from the browser back to the client.
String msgContent = "Message \"" + payload + "\" has been received by server.";
try {
// connection.send(msgContent); Deprecated
connection.getBasicRemote().sendText(msgContent); //JSR 356
} catch (IOException e) {
// connection.close(ClosingMessage.SC_GOING_AWAY); Deprecated
connection.close(new //JSR 356
CloseReason(CloseReason.CloseCodes.GOING_AWAY, "Going away.")); //JSR 356
}
}
}
例18-42では、WebLogic ServerでJava API for WebSocketを使用します。この例では、ユーザーがクライアントから送信したテキストを、サーバー・エンドポイントでエコーします。ユーザーがテキスト・メッセージを送信すると、サーバーはそのメッセージにテキスト(from your server)を追加してユーザーにメッセージを返信します。
例18-42 WebLogic ServerでのJava API for WebSocketの使用方法
package com.example.websocket.sample.echo;
import java.io.IOException;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnOpen
public void onOpen(Session session) throws IOException {
session.getBasicRemote().sendText("onOpen is invoked.");
}
@OnMessage
public String echo(String message) {
return message + " (from server)";
}
@OnError
public void onError(Throwable t) {
t.printStackTrace();
}
}