@Qualifier @RetentionSE(valueSE=RUNTIMESE) @TargetSE(valueSE={METHODSE,FIELDSE,PARAMETERSE}) public @interface Push
CDI アノテーション @Push を使用すると、WAR 内のコンテナー管理のアーティファクトに、特定の <f:websocket> チャネルに関連付けられた PushContext を挿入できます。
@Inject @Push private PushContext channelName;
まず、web.xml のブールコンテキストパラメーターで Web ソケットエンドポイントを有効にします。
<context-param>
<param-name>jakarta.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>
<param-value>true</param-value>
</context-param>
少なくとも channel 名と onmessage JavaScript リスナー関数を使用して、Jakarta Server Faces ビューで <f:websocket> タグを宣言します。チャネル名は Jakarta Expression Language 式ではなく、英数字、ハイフン、アンダースコア、ピリオドのみを含めることができます。
これは、既存の JavaScript リスナー関数を参照する例です。
<f:websocket channel="someChannel" onmessage="someWebsocketListener" />
function someWebsocketListener(message, channel, event) {
console.log(message);
}
インライン JavaScript リスナー関数を宣言する例を次に示します。
<f:websocket channel="someChannel" onmessage="function(message) { console.log(message); }" />
onmessage JavaScript リスナー関数は、次の 3 つの引数で呼び出されます。
message: JSON オブジェクトとしてのプッシュメッセージ。channel: チャンネル名。event: 生の MessageEvent インスタンス。 サーバーが HTTP コンテナーとは異なる TCP ポートで WS コンテナーを実行するように構成されている場合は、web.xml のオプションの jakarta.faces.WEBSOCKET_ENDPOINT_PORT 整数コンテキストパラメーターを使用して、ポートを明示的に指定できます。
<context-param>
<param-name>jakarta.faces.WEBSOCKET_ENDPOINT_PORT</param-name>
<param-value>8000</param-value>
</context-param>
正常に接続されると、ドキュメントが開いている限り Web ソケットはデフォルトで開いており、接続が閉じられたり中止されたりすると、間隔を広げて自動再接続します。ネットワークエラーまたはサーバーの再起動。最初の接続試行がすでに失敗した場合、自動再接続は行われません。ドキュメントがアンロードされると、Web ソケットは暗黙的に閉じられます。
WAR 側では、@Named や @WebServlet などの CDI/ コンテナー管理下のアーティファクトの中で、プッシュメッセージを送信したい場所の指定されたチャネル名に @Push アノテーションを介して PushContext を注入し、プッシュメッセージを表す任意の Java オブジェクトで PushContext.send(Object) を起動します。
@Inject
@Push
private PushContext someChannel;
public void sendMessage(Object message) {
someChannel.send(message);
}
デフォルトでは、チャネルの名前は、注入が行われる変数の名前から取得されます。チャネル名は、channel 属性を介してオプションで指定できます。以下の例では、チャネル名 foo のプッシュコンテキストを bar という名前の変数に挿入します。
@Inject @Push(channel = "foo") private PushContext bar;
メッセージオブジェクトは JSON としてエンコードされ、channel 名に関連付けられた onmessage JavaScript リスナー関数の message 引数として配信されます。プレーンなバニラ String にすることもできますが、コレクション、マップ、さらには javabean にすることもできます。
Web ソケットは双方向通信をサポートしていますが、<f:websocket> プッシュは、サーバーからクライアントへの一方向通信用に設計されています。クライアントからサーバーにデータを送信する場合は、通常の方法で Jakarta Server Faces ajax を引き続き使用してください。これには、特に Jakarta Server Faces ビューステート、HTTP セッション、重要なことに、ビジネスサービスメソッドのすべてのセキュリティ制約を維持するという利点があります。
デフォルトでは、Web ソケットは application スコープです。つまり、同じ Web ソケットチャネルが開いている Web アプリケーション全体のビュー / セッションは、同じプッシュメッセージを受信します。プッシュメッセージは、すべてのユーザーとアプリケーション自体が送信できます。
オプションの scope 属性を session に設定して、プッシュメッセージを現在のユーザーセッションのすべてのビューのみに制限できます。プッシュメッセージは、ユーザー自身のみが送信でき、アプリケーションは送信できません。
<f:websocket channel="someChannel" scope="session" ... />
scope 属性を view に設定して、プッシュメッセージを現在のビューのみに制限することもできます。プッシュメッセージは、同じ URL であっても、同じセッションの他のビューには表示されません。プッシュメッセージは、ユーザー自身のみが送信でき、アプリケーションは送信できません。
<f:websocket channel="someChannel" scope="view" ... />
scope 属性は Jakarta Expression Language 式ではない可能性があり、許可される値は application、session、view であり、大文字と小文字は区別されません。
さらに、オプションの user 属性は、ログインしたユーザーの一意の識別子(通常はログイン名またはユーザー ID)に設定できます。このようにして、プッシュメッセージは特定のユーザーをターゲットにすることができ、他のユーザーやアプリケーション自体からも送信できます。user 属性の値は、少なくとも SerializableSE を実装し、メモリフットプリントが小さい必要があるため、ユーザーエンティティ全体を配置することはお勧めしません。
E.g。コンテナー管理認証または関連するフレームワーク / ライブラリを使用している場合:
<f:websocket channel="someChannel" user="#{request.remoteUser}" ... />
または、Jakarta Expression Language に #{someLoggedInUser} としてカスタムユーザーエンティティがあり、その識別子を表す id プロパティがある場合:
<f:websocket channel="someChannel" user="#{someLoggedInUser.id}" ... />
user 属性が指定されている場合、scope はデフォルトで session に設定され、application に設定することはできません。
サーバー側では、プッシュメッセージは PushContext.send(Object, Serializable) を介して user 属性で指定されたユーザーをターゲットにすることができます。プッシュメッセージは、すべてのユーザーとアプリケーション自体が送信できます。
@Inject
@Push
private PushContext someChannel;
public void sendMessage(Object message, User recipientUser) {
Long recipientUserId = recipientUser.getId();
someChannel.send(message, recipientUserId);
}
ユーザー ID を保持する CollectionSE を PushContext.send(Object, Collection) に渡すことにより、複数のユーザーをターゲットにすることができます。
public void sendMessage(Object message, Group recipientGroup) {
Collection<Long> recipientUserIds = recipientGroup.getUserIds();
someChannel.send(message, recipientUserIds);
}
オプションの connected 属性を使用して、Web ソケットを自動接続するかどうかを制御できます。
<f:websocket ... connected="#{bean.pushable}" />
デフォルトは true であり、Web ソケットプッシュ接続を開くか閉じるかは JavaScript 命令として解釈されます。値が Jakarta Expression Language 式であり、ajax リクエスト中に false になる場合、プッシュ接続は、その ajax リクエストの完了時に明示的に閉じられます。
また、明示的に false に設定し、jsf.push.open(clientId) を呼び出してコンポーネントのクライアント ID を渡すことにより、クライアント側でプッシュ接続を手動で開くこともできます。
<h:commandButton ... onclick="jsf.push.open('foo')">
<f:ajax ... />
</h:commandButton>
<f:websocket id="foo" channel="bar" scope="view" ... connected="false" />
1 回限りのプッシュを予定していて、それ以上のメッセージを期待しない場合は、オプションで、jsf.push.close(clientId) を呼び出し、コンポーネントのクライアント ID を渡すことにより、クライアント側からプッシュ接続を明示的に閉じることができます。例: onmessage の JavaScript リスナー関数は次のとおりです。
function someWebsocketListener(message) {
// ...
jsf.push.close('foo');
}
オプションの onopen JavaScript リスナー関数を使用して、クライアント側で Web ソケットを開いたときにリッスンできます。これは、成功するかどうかに関係なく、最初の接続試行で呼び出されます。これは、最初の接続が成功した後、Web ソケットが壊れた接続を自動再接続するときに呼び出されません。
<f:websocket ... onopen="websocketOpenListener" />
function websocketOpenListener(channel) {
// ...
}
onopen JavaScript リスナー関数は、次の 1 つの引数で呼び出されます。
channel: チャネル名。グローバルリスナーを使用する場合に役立ちます。 オプションの onclose JavaScript リスナー関数を使用して、Web ソケットの(異常な)通常のクローズをリッスンできます。これは、最初の接続試行が失敗した場合、サーバーがクローズ理由コード 1000 (通常のクローズ)または 1008 (ポリシー違反)を返した場合、または再接続の最大試行回数を超えた場合に呼び出されます。これは、最初の接続が成功した後、Web ソケットが切断された接続に対して自動再接続を試行できる場合は呼び出されません。
<f:websocket ... onclose="websocketCloseListener" />
function websocketCloseListener(code, channel, event) {
if (code == -1) {
// Web sockets not supported by client.
} else if (code == 1000) {
// Normal close (as result of expired session or view).
} else {
// Abnormal close reason (as result of an error).
}
}
onclose JavaScript リスナー関数は、次の 3 つの引数で呼び出されます。
code: 整数としてのクローズ理由コード。これが -1 の場合、Web ソケットはクライアントによってサポートされていません。これが 1000 の場合、通常は閉じていました。それ以外の場合、これが 1000 でない場合は、エラーが発生している可能性があります。すべてのクローズコードの詳細なリストについては、RFC6455 セクション 7.4.1 および CloseReason.CloseCodes API も参照してください。channel: チャンネル名。event: 生の CloseEvent インスタンス。 セッションまたはビュースコープのソケットが、サーバーによってクローズ理由コード 1000 で自動的に閉じられた場合(したがって、jsf.push.close(clientId) を介してクライアントによって手動で閉じられた場合)、セッションまたはビューが期限切れになったことを意味します。
Web ソケットが開かれると、新しい CDI WebsocketEvent が @WebsocketEvent.Opened 修飾子を使用して起動されます。Web ソケットが閉じられると、新しい CDIWebsocketEvent が @WebsocketEvent.Closed 修飾子を使用して起動されます。これらは、以下のように、アプリケーションスコープの CDIBean でのみ観測および収集できます。
@ApplicationScoped
public class WebsocketObserver {
public void onOpen(@Observes @Opened WebsocketEvent event) {
String channel = event.getChannel(); // Returns <f:websocket channel>.
Long userId = event.getUser(); // Returns <f:websocket user>, if any.
// ...
}
public void onClose(@Observes @Closed WebsocketEvent event) {
String channel = event.getChannel(); // Returns <f:websocket channel>.
Long userId = event.getUser(); // Returns <f:websocket user>, if any.
CloseCode code = event.getCloseCode(); // Returns close reason code.
// ...
}
}
特定のロールを持つログインユーザーのみに制限されているページでソケットが宣言されている場合は、プッシュハンドシェイクリクエスト URL の URL を制限された URL のセットに追加することをお勧めします。
プッシュハンドシェイクリクエスト URL は、URI プレフィックス /jakarta.faces.push/ とそれに続くチャネル名で構成されます。たとえば、コンテナー管理セキュリティの場合、サンプルページ /user/foo.xhtml を、以下のように web.xml のサンプル URL パターン /user/* でサンプルロール USER を持つログインユーザーにすでに制限しています。
<security-constraint>
<web-resource-collection>
<web-resource-name>Restrict access to role USER.</web-resource-name>
<url-pattern>/user/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>USER</role-name>
</auth-constraint>
</security-constraint>
.. そしてページ /user/foo.xhtml に <f:websocket channel="foo"> が含まれている場合は、以下のように /jakarta.faces.push/foo のプッシュハンドシェイクリクエスト URL パターンに制限を追加する必要があります。
<security-constraint>
<web-resource-collection>
<web-resource-name>Restrict access to role USER.</web-resource-name>
<url-pattern>/user/*</url-pattern>
<url-pattern>/jakarta.faces.push/foo</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>USER</role-name>
</auth-constraint>
</security-constraint>
追加のセキュリティとして、特にセキュリティの制約によって制限できないパブリックチャネルの場合、<f:websocket> はこれまでに宣言されたすべてのチャネルを現在の HTTP セッションに登録し、受信 Web ソケットオープンリクエストがこれまでに一致するかどうかをチェックします。現在の HTTP セッションに登録されているチャネル。チャネルが不明な場合(たとえば、エンドユーザーによってランダムに推測またはスプーフィングされたり、セッションの有効期限が切れた後に手動で再接続されたりした場合)、Web ソケットはクローズ理由コード CloseReason.CloseCodes.VIOLATED_POLICY(1008)ですぐに閉じられます。また、HTTP セッションが破棄されると、まだ開いているすべてのセッションおよびビュースコープのチャネルが、クローズ理由コード CloseReason.CloseCodes.NORMAL_CLOSURE(1000)でサーバー側から明示的に閉じられます。クライアント側のページに関連付けられたセッションまたはビューが期限切れになった場合でも、アプリケーションスコープのソケットのみが開いたままであり、サーバー側から到達可能です。
受信したプッシュメッセージに応じて複雑な UI 更新を実行する場合は、<f:websocket> 内に <f:ajax> をネストできます。次に例を示します。
<h:panelGroup id="foo">
... (some complex UI here) ...
</h:panelGroup>
<h:form>
<f:websocket channel="someChannel" scope="view">
<f:ajax event="someEvent" listener="#{bean.pushed}" render=":foo" />
</f:websocket>
</h:form>
ここで、プッシュメッセージは単に ajax イベント名を表しています。任意のカスタムイベント名を使用できます。
someChannel.send("someEvent");
別の方法は、<w:websocket> を <h:commandScript> と組み合わせることです。例:
<h:panelGroup id="foo">
... (some complex UI here) ...
</h:panelGroup>
<f:websocket channel="someChannel" scope="view" onmessage="someCommandScript" />
<h:form>
<h:commandScript name="someCommandScript" action="#{bean.pushed}" render=":foo" />
</h:form>
Map<String,V> または JavaBean をプッシュメッセージオブジェクトとして渡すと、すべてのエントリ / プロパティがコマンドスクリプトメソッド #{bean.pushed} のリクエストパラメーターとして透過的に使用可能になります。
PushContext, UIWebsocket, WebsocketEventpublic abstract StringSE channel
Copyright © 2018,2020 Eclipse Foundation.
Use is subject to license terms.