第14章 Jakarta WebSocket アプリケーションの作成
Jakarta WebSocket プロトコルは、Web クライアントとサーバー間の双方向通信を提供します。クライアントとサーバー間の通信はイベントベースであるため、ポーリングベースの通信よりも処理が高速になり、帯域幅が小さくなります。Jakarta WebSocket は、JavaScript API を用いて Web アプリケーションで使用したり、Jakarta Websocket 仕様を用いてクライアント Jakarta WebSocket エンドポイントで使用したりできます。
最初に接続はクライアントとサーバー間で HTTP 接続として確立されます。その後、クライアントは Upgrade
ヘッダーを使用して Jakarta WebSocket 接続を要求します。同じ TCP/IP 接続上ではすべて全二重通信になり、データのオーバヘッドが最小化されます。各メッセージには不必要な HTTP ヘッダーコンテンツが含まれていないため、Jakarta WebSocket 通信で必要な帯域幅は小さくなります。その結果、通信パスのレイテンシーが低くなるため、リアルタイムの応答が必要なアプリケーションに適しています。
JBoss EAP Jakarta WebSocket 実装は、サーバーエンドポイントに対して完全な依存関係注入サポートを提供しますが、クライアントエンドポイントに対して Contexts and Dependency Injection サービスを提供しません。
Jakarta WebSocket アプリケーションには以下のコンポーネントと設定変更が必要です。
- Java クライアントまたは Jakarta WebSocket が有効になっている HTML クライアント。HTML クライアントのブラウザーサポートは、http://caniuse.com/#feat=websockets を参照してください。
- Jakarta WebSocket サーバーエンドポイントクラス。
- Jakarta WebSocket API で依存関係を宣言するために設定されたプロジェクト依存関係。
Jakarta WebSocket アプリケーションの作成
以下のコード例は、JBoss EAP に同梱される websocket-hello
クイックスタートの一部です。これは、接続を開き、メッセージを送信し、接続を閉じる Jakarta WebSocket アプリケーションの単純な例です。他の機能を実装したり、実際のアプリケーションで必要となるエラー処理を含むことはありません。
JavaScript HTML クライアントを作成します。
以下は Jakarta WebSocket クライアントの例になります。この例には 3 つの JavaScript 関数が含まれています。
-
connect()
: この関数は、Jakarta WebSocket URI を渡す Jakarta WebSocket 接続を作成します。リソースの場所は、サーバーエンドポイントクラスに定義されたリソースと一致します。この関数は、Jakarta WebSocket のonopen
、onmessage
、onerror
、およびonclose
もインターセプトし、処理します。 -
sendMessage()
: この関数はフォームに入力された名前を取得し、メッセージを作成します。 さらに、WebSocket.send() コマンドを使用してメッセージを送信します。 -
disconnect()
: この関数は WebSocket.close() コマンドを実行します。 -
displayMessage()
: この関数は、ページ上の表示メッセージを Jakarta WebSocket エンドポイントメソッドによって返された値に設定します。 displayStatus()
: この関数は Jakarta WebSocket の接続状態を表示します。例: アプリケーション
index.html
コード<html> <head> <title>WebSocket: Say Hello</title> <link rel="stylesheet" type="text/css" href="resources/css/hello.css" /> <script type="text/javascript"> var websocket = null; function connect() { var wsURI = 'ws://' + window.location.host + '/websocket-hello/websocket/helloName'; websocket = new WebSocket(wsURI); websocket.onopen = function() { displayStatus('Open'); document.getElementById('sayHello').disabled = false; displayMessage('Connection is now open. Type a name and click Say Hello to send a message.'); }; websocket.onmessage = function(event) { // log the event displayMessage('The response was received! ' + event.data, 'success'); }; websocket.onerror = function(event) { // log the event displayMessage('Error! ' + event.data, 'error'); }; websocket.onclose = function() { displayStatus('Closed'); displayMessage('The connection was closed or timed out. Please click the Open Connection button to reconnect.'); document.getElementById('sayHello').disabled = true; }; } function disconnect() { if (websocket !== null) { websocket.close(); websocket = null; } message.setAttribute("class", "message"); message.value = 'WebSocket closed.'; // log the event } function sendMessage() { if (websocket !== null) { var content = document.getElementById('name').value; websocket.send(content); } else { displayMessage('WebSocket connection is not established. Please click the Open Connection button.', 'error'); } } function displayMessage(data, style) { var message = document.getElementById('hellomessage'); message.setAttribute("class", style); message.value = data; } function displayStatus(status) { var currentStatus = document.getElementById('currentstatus'); currentStatus.value = status; } </script> </head> <body> <div> <h1>Welcome to Red Hat JBoss Enterprise Application Platform!</h1> <div>This is a simple example of a Jakarta WebSocket implementation.</div> <div id="connect-container"> <div> <fieldset> <legend>Connect or disconnect using websocket :</legend> <input type="button" id="connect" onclick="connect();" value="Open Connection" /> <input type="button" id="disconnect" onclick="disconnect();" value="Close Connection" /> </fieldset> </div> <div> <fieldset> <legend>Type your name below, then click the `Say Hello` button :</legend> <input id="name" type="text" size="40" style="width: 40%"/> <input type="button" id="sayHello" onclick="sendMessage();" value="Say Hello" disabled="disabled"/> </fieldset> </div> <div>Current WebSocket Connection Status: <output id="currentstatus" class="message">Closed</output></div> <div> <output id="hellomessage" /> </div> </div> </div> </body> </html>
-
Jakarta WebSocket サーバーエンドポイントを作成します。
以下の方法のいずれかを使用して Jakarta WebSocket サーバーエンドポイントを作成できます。
-
Programmatic Endpoint
: エンドポイントは Endpoint クラスを拡張します。 -
Annotated Endpoint
: エンドポイントクラスはアノテーションを使用して Jakarta WebSocket イベントと対話します。これは、プログラム的なエンドポイントよりも簡単にコーディングできます。
以下のコード例では、アノテーション付きエンドポイントが使用され、以下のイベントが処理されます。
-
@ServerEndpoint
アノテーションは、このクラスを Jakarta WebSocket サーバーエンドポイントとして識別し、パスを指定します。 -
Jakarta WebSocket 接続が開かれると
@OnOpen
アノテーションがトリガーされます。 -
メッセージが受信されると、
@OnMessage
アノテーションがトリガーされます。 Jakarta WebSocket 接続が閉じられると
@OnClose
アノテーションがトリガーされます。例: Jakarta WebSocket エンドポイントコード
package org.jboss.as.quickstarts.websocket_hello; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/websocket/helloName") public class HelloName { @OnMessage public String sayHello(String name) { System.out.println("Say hello to '" + name + "'"); return ("Hello" + name); } @OnOpen public void helloOnOpen(Session session) { System.out.println("WebSocket opened: " + session.getId()); } @OnClose public void helloOnClose(CloseReason reason) { System.out.println("WebSocket connection closed with CloseCode: " + reason.getCloseCode()); } }
-
プロジェクト POM ファイルで Jakarta WebSocket API の依存関係を宣言します。
Maven を使用する場合は、プロジェクト
pom.xml
ファイルに以下の依存関係を追加します。例: Maven 依存関係
<dependency> <groupId>org.jboss.spec.javax.websocket</groupId> <artifactId>jboss-websocket-api_1.1_spec</artifactId> <scope>provided</scope> </dependency>
JBoss EAP に同梱されるクイックスタートには、追加の Jakarta WebSocket クライアントとエンドポイントのコード例が含まれます。