The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

WebSockets Next リファレンスガイド

この技術は、previewと考えられています。

preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリストGitHub の課題管理 で受け付けています。

とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。

quarkus-websockets-next エクステンションは、WebSocket サーバーおよびクライアントエンドポイントを定義するための最新の宣言型 API を提供します。

1. WebSocket プロトコル

RFC6455 に記載されている WebSocket プロトコルは、単一の TCP 接続を介してクライアントとサーバー間の双方向通信チャネルを作成するための標準化された方法を確立します。 HTTP とは異なり、WebSocket は別個の TCP プロトコルとして動作しますが、HTTP とシームレスに連携して機能するように設計されています。 たとえば、同じポートを再利用し、同じセキュリティーメカニズムと互換性があります。

WebSocket を使用したやり取りは、WebSocket プロトコルに移行するための 'Upgrade' ヘッダーを使用する HTTP リクエストで開始されます。 サーバーは、 200 OK レスポンスの代わりに 101 Switching Protocols レスポンスを返し、HTTP 接続を WebSocket 接続へとアップグレードします。 このハンドシェイクが成功すると、最初の HTTP アップグレードリクエストで使用された TCP ソケットは開いたままとなり、クライアントとサーバーの双方が継続的に双方向のメッセージをやり取りできるようになります。

2. HTTP および WebSocket アーキテクチャースタイル

WebSocket は HTTP と互換性があり、HTTP リクエストを通じて開始されますが、2 つのプロトコルは異なるアーキテクチャーとプログラミングモデルを導くため、その違いを認識することが重要です。

HTTP/REST では、アプリケーションはリソース/エンドポイントを中心に構成され、さまざまな HTTP メソッドやパスを処理します。 クライアントとのやり取りは、適切なメソッドとパスを指定した HTTP リクエストを送信することで行われ、リクエスト/レスポンスのパターンに従います サーバーは、パス、メソッド、ヘッダーに基づいて、受信したリクエストを対応するハンドラーにルーティングし、明確に定義されたレスポンスで返します。

逆に、WebSocket では通常、最初の HTTP 接続に単一のエンドポイントが使用され、その後、すべてのメッセージが同じ TCP 接続を利用します。 これにより、非同期かつメッセージ駆動型のまったく異なるインタラクションモデルが導入されます。

WebSocket は、HTTP とは対照的に、低レベルのトランスポートプロトコルです。 メッセージの形式、ルーティング、または処理には、メッセージのセマンティクスに関するクライアントとサーバー間の事前の合意が必要です。

WebSocket クライアントとサーバーの場合、HTTP ハンドシェイクリクエストの Sec-WebSocket-Protocol ヘッダーにより、より高レベルのメッセージングプロトコルのネゴシエーションが可能になります。このヘッダーがない場合、サーバーとクライアントは独自の規則を確立する必要があります。

3. Quarkus WebSockets と Quarkus WebSockets Next

このガイドでは、従来の quarkus-websockets エクステンションに比べ、効率性と使いやすさが向上した WebSocket API の実装である quarkus-websockets-next エクステンションを利用します。 オリジナルの quarkus-websockets エクステンションは引き続きアクセス可能で、継続的なサポートが提供されますが、機能開発が行われる可能性は低いです。

quarkus-websockets とは異なり、 quarkus-websockets-next エクステンションは Jakarta WebSocket 仕様を 実装していません。 代わりに、使いやすさを重視した最新の API を導入しています。 さらに、Quarkus のリアクティブアーキテクチャーおよびネットワーク層とシームレスに統合するように調整されています。

Quarkus WebSockets Next エクステンションで使用されるアノテーションは、同じ名前を共有する場合もありますが、JSR 356 のアノテーションとは異なります。 JSR アノテーションには、Quarkus WebSockets Next エクステンションが従わないセマンティクスが含まれています。

4. プロジェクトのセットアップ

websockets-next エクステンションを使用するには、プロジェクトに io.quarkus:quarkus-websockets-next 依存関係を追加する必要があります。

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets-next</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-websockets-next")

5. Endpoints

サーバー API and クライアント API は両方とも、メッセージを消費および送信するために使用される エンドポイント を定義します。 エンドポイントは CDI Bean として実装され、注入をサポートします。 エンドポイントは、 @OnTextMessage@OnBinaryMessage@OnPingMessage@OnPongMessage@OnOpen@OnClose および @OnError アノテーションが付けられた callback methods を宣言します。 これらのメソッドは、さまざまな WebSocket イベントを処理するために使用されます。 通常、接続されたクライアントがサーバーにメッセージを送信するとき、およびサーバーが接続されたクライアントにメッセージを送信するときに、 @OnTextMessage アノテーションが付けられたメソッドが呼び出されます。

クライアント API には、新しい WebSocket 接続を設定および作成するために使用される connectors も含まれています。

5.1. サーバーエンドポイント

サーバーエンドポイントは、 @io.quarkus.websockets.next.WebSocket アノテーションが付けられたクラスです。 WebSocket#path() の値は、エンドポイントのパスを定義するために使用されます。

package org.acme.websockets;

import io.quarkus.websockets.next.WebSocket;
import jakarta.inject.Inject;

@WebSocket(path = "/chat/{username}") (1)
public class ChatWebSocket {

}

したがって、クライアントは ws://localhost:8080/chat/your-name を使用して、この Web ソケットエンドポイントに接続できます。 TLS が使用されている場合、URL は wss://localhost:8443/chat/your-name です。

エンドポイントパスは、 quarkus.http.root-path (デフォルトでは /) によって設定された root context に対して相対的です。たとえば、 application.propertiesquarkus.http.root-path=/api を追加すると、クライアントは http://localhost:8080/api/chat/the-name を使用してこのエンドポイントに接続できます。

5.2. クライアントエンドポイント

クライアントエンドポイントは、 @io.quarkus.websockets.next.WebSocketClient アノテーションが付けられたクラスです。 WebSocketClient#path() の値は、このクライアントが接続されるエンドポイントのパスを定義するために使用されます。

package org.acme.websockets;

import io.quarkus.websockets.next.WebSocketClient;
import jakarta.inject.Inject;

@WebSocketClient(path = "/chat/{username}") (1)
public class ChatWebSocket {

}
クライアントエンドポイントは、メッセージの受信と送信に使用されます。新しい WebSocket 接続を設定して開くには、connectors API が必要です。

5.3. パスパラメーター

WebSocket エンドポイントのパスには、パスパラメーターを含めることができます。 構文は JAX-RS リソースの場合と同じです: {parameterName}

パスパラメーター値には、それぞれ io.quarkus.websockets.next.WebSocketConnection#pathParam(String) メソッドまたは io.quarkus.websockets.next.WebSocketClientConnection#pathParam(String) を使用してアクセスできます。 あるいは、 @io.quarkus.websockets.next.PathParam アノテーションが付与されたエンドポイントコールバックメソッドパラメーターが自動的に注入されます。

WebSocketConnection#pathParam(String) の例
@Inject io.quarkus.websockets.next.WebSocketConnection connection;
// ...
String value = connection.pathParam("parameterName");

パスパラメーターの値は常に文字列です。 パス内にパスパラメーターが存在しない場合、 WebSocketConnection#pathParam(String)/WebSocketClientConnection#pathParam(String) メソッドは null を返します。 @PathParam アノテーションが付けられたエンドポイントコールバックメソッドパラメーターがあり、パラメーター名がエンドポイントパスで定義されていない場合、ビルドは失敗します。

クエリーパラメーターはサポートされていません。ただし、 WebSocketConnection#handshakeRequest().query() を使用してクエリーにアクセスできます。

5.4. CDI スコープ

エンドポイントは CDI Bean として管理されます。 デフォルトでは、 @Singleton スコープが使用されます。 ただし、開発者は特定の要件に合わせて代替スコープを指定できます。

@Singleton および @ApplicationScoped エンドポイントは、すべての WebSocket 接続間で共有されます。 したがって、実装はステートレスまたはスレッドセーフのいずれかである必要があります。

5.4.1. セッションコンテキスト

エンドポイントに @SessionScoped アノテーションが付与されている場合、またはエンドポイントが直接的または間接的に @SessionScoped Bean に依存している場合、各 WebSocket 接続は独自の セッションコンテキスト に関連付けられます。 セッションコンテキストは、エンドポイントコールバックの呼び出し中にアクティブになります。 同じ接続内で後続の コールバックメソッド が呼び出される場合、同じセッションコンテキストが利用されます。 セッションコンテキストは、接続が閉じられるまで (通常は @OnClose メソッドの実行が完了したとき) アクティブなままになり、接続が閉じられた時点で終了します。

quarkus.websockets-next.server.activate-session-context 設定プロパティーを always に設定することもできます。この場合、 @SessionScoped Bean が依存関係ツリーに参加しているかどうかに関係なく、セッションコンテキストは常にアクティブ化されます。
@SessionScoped エンドポイント
import jakarta.enterprise.context.SessionScoped;

@WebSocket(path = "/ws")
@SessionScoped (1)
public class MyWebSocket {

}
1 このサーバーエンドポイントは共有されず、セッション/接続に限定されます。

5.4.2. リクエストコンテキスト

エンドポイントが以下の場合、各 WebSocket エンドポイントコールバックメソッドの実行は、新しい CDI リクエストコンテキスト に関連付けられます。

  • @RequestScoped アノテーションが付けられています。

  • @RolesAllowed などのセキュリティーアノテーションが付けられたメソッドがあります。

  • @RequestScoped Bean に直接的または間接的に依存します。

  • 標準のセキュリティーアノテーションで保護された CDI Bean に直接的または間接的に依存します。

quarkus.websockets-next.server.activate-request-context 設定プロパティーを always に設定することもできます。この場合、エンドポイントコールバックが呼び出されると、リクエストコンテキストが常にアクティブ化されます。
@RequestScoped エンドポイント
import jakarta.enterprise.context.RequestScoped;

@WebSocket(path = "/ws")
@RequestScoped (1)
public class MyWebSocket {

}
1 このサーバーエンドポイントは、コールバックメソッドの実行ごとにインスタンス化されます。

5.5. コールバックメソッド

WebSocket エンドポイントは以下を宣言できます。

  • 最大 1 つの @OnTextMessage メソッド: 接続されたクライアント/サーバーからのテキストメッセージを処理します。

  • 最大 1 つの @OnBinaryMessage メソッド: 接続されたクライアント/サーバーからのバイナリーメッセージを処理します。

  • 最大 1 つの @OnPingMessage メソッド: 接続されたクライアント/サーバーからの ping メッセージを処理します。

  • 最大 1 つの @OnPongMessage メソッド: 接続されたクライアント/サーバーからの pong メッセージを処理します。

  • 最大 1 つの @OnOpen メソッド: 接続が開かれたときに呼び出されます。

  • 最大 1 つの @OnClose メソッド: 接続が閉じられたときに実行されます。

  • 任意の数の @OnError メソッド: エラーが発生したときに呼び出されます。つまり、エンドポイントコールバックがランタイムエラーをスローしたとき、変換エラーが発生したとき、または返された io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi が失敗を受け取ったときです。

一部のエンドポイントのみにすべてのメソッドを含める必要があります。 ただし、少なくとも @On[Text|Binary]Message または @OnOpen が含まれている必要があります。

いずれかのエンドポイントがこれらのルールに違反すると、ビルド時にエラーがスローされます。 sub-websocket を表す静的なネストされたクラスは同じガイドラインに従います。

WebSocket エンドポイントの外部で @OnTextMessage@OnBinaryMessage@OnOpen、および @OnClose アノテーションが付与されたメソッドはエラーとみなされ、適切なエラーメッセージが表示されてビルドが失敗します。

5.6. メッセージの処理

クライアントからメッセージを受信するメソッドには、 @OnTextMessage または @OnBinaryMessage アノテーションが付けられます。

OnTextMessage は、クライアントから受信されるすべての テキスト メッセージに対して呼び出されます。 OnBinaryMessage は、クライアントが受信するすべての バイナリー メッセージに対して呼び出されます。

5.6.1. 呼び出しルール

コールバックメソッドを呼び出すと、WebSocket 接続にリンクされた セッション スコープはアクティブなままになります。 さらに、リクエストスコープはメソッドが完了するまで (または非同期メソッドとリアクティブメソッドの場合は結果が生成されるまで) アクティブです。

WebSocket Next は、Quarkus REST に似た、メソッドの戻り値のタイプと @Blocking@NonBlocking などの追加のアノテーションで決定される、ブロッキングノンブロッキング ロジックをサポートします。

実行に関するルールは次のとおりです。

  • @RunOnVirtualThread@Blocking、または @Transactional アノテーションが付与されたメソッドは、ブロッキングとみなされます。

  • @RunOnVirtualThread アノテーションが付けられたクラスで宣言されたメソッドは、ブロッキングとみなされます。

  • @NonBlocking アノテーションが付けられたメソッドは、ノンブロッキングとみなされます。

  • @Transactional アノテーションが付けられたクラスで宣言されたメソッドは、 @NonBlocking アノテーションが付けられていない限り、ブロッキングとみなされます。

  • メソッドが上記のアノテーションのいずれも宣言していない場合、実行モデルは戻り値の型から派生します。

    • Uni および Multi を返すメソッドは、ノンブロッキングとみなされます。

    • void またはその他のタイプを返すメソッドは、ブロッキングとみなされます。

  • Kotlin の suspend 関数は常にノンブロッキングとみなされるため、 @Blocking@NonBlocking@RunOnVirtualThread アノテーションのいずれかを付与することはできません。 また、 @RunOnVirtualThread アノテーションが付けられたクラス内に含めることもできません。

  • ノンブロッキングメソッドは、接続のイベントループスレッドで実行する必要があります。

  • ブロッキングメソッドは、 @RunOnVirtualThread アノテーションが付けられている場合、または、 @RunOnVirtualThread アノテーションがクラスに付けられている場合を除き、 ワーカースレッド上で実行される必要があります。

  • @RunOnVirtualThread アノテーションが付与されたメソッド、または @RunOnVirtualThread アノテーションが付与されたクラスで宣言されたメソッドは、 仮想スレッド上で実行される必要があります。仮想スレッドは、呼び出しのたび新しく生成されます。

5.6.2. メソッドパラメーター

メソッドは、メッセージパラメーターを 1 つのみ受け入れる必要があります。

  • メッセージオブジェクト (任意のタイプ)。

  • メッセージタイプが X の Multi<X>

ただし、次のパラメーターも受け入れる場合があります。

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • @PathParam アノテーションが付けられた String パラメーター

メッセージオブジェクトは送信されたデータを表し、raw のコンテンツ (StringJsonObjectJsonArrayBuffer、または byte[]) またはデシリアライズされた高レベルオブジェクトとしてアクセスできます。後者のアプローチが推奨されます。

Multi を受信すると、メソッドは接続ごとに 1 回呼び出され、提供された Multi はこの接続によって送信された項目を受信します。 メソッドが Multi (受信したものから構築) を返す場合、Quarkus は自動的にそれをサブスクライブし、完了、失敗、またはキャンセルされるまで、発行された項目を書き込みます。 ただし、メソッドが Multi を返さない場合は、データを使用するために受信した Multi をサブスクライブする必要があります。

2 つの例があります。

// No need to subscribe to the incoming Multi as the method returns a Multi derived from the incoming one
@OnTextMessage
public Multi<ChatMessage> stream(Multi<ChatMessage> incoming) {
    return incoming.log();
}

// ...

// Must subscribe to the incoming Multi as the method does not return a Multi, otherwise no data will be consumed
@OnTextMessage
public void stream(Multi<ChatMessage> incoming) {
    incoming.subscribe().with(item -> log(item));
}

受信した Multi のサブスクライブに関する詳細は、Uni または Multi をサブスクライブするタイミング を参照してください。

5.6.3. サポートされている戻り値のタイプ

@OnTextMessage または @OnBinaryMessage アノテーションが付与されたメソッドは、WebSocket 通信を効率的に処理するためにさまざまなタイプを返すことができます。

  • void: 明示的なレスポンスがクライアントに返されないブロッキングメソッドを示します。

  • Uni<Void>: 返された Uni の完了が処理の終了を意味するノンブロッキングメソッドを示します。明示的なレスポンスはクライアントに返されません。

  • X タイプのオブジェクトは、返されたオブジェクトがシリアライズされ、レスポンスとしてクライアントに送り返されるブロッキングメソッドを表します。

  • Uni<X>: null 以外の Uni によって発行された項目がレスポンスとしてクライアントに送信されるノンブロッキングメソッドを指定します。

  • Multi<X>: null 以外の Multi によって発行された項目が完了またはキャンセルされるまでクライアントに順番に送信されるノンブロッキングメソッドを示します。

  • Unit を返す Kotlin の suspend 関数: 明示的なレスポンスがクライアントに返されないノンブロッキングメソッドを示します。

  • X を返す Kotlin の suspend 関数: 返された項目がレスポンスとしてクライアントに送信されるノンブロッキングメソッドを指定します。

これらの方法の例をいくつか示します。

@OnTextMessage
void consume(Message m) {
// Process the incoming message. The method is called on an executor thread for each incoming message.
}

@OnTextMessage
Uni<Void> consumeAsync(Message m) {
// Process the incoming message. The method is called on an event loop thread for each incoming message.
// The method completes when the returned Uni emits its item.
}

@OnTextMessage
ResponseMessage process(Message m) {
// Process the incoming message and send a response to the client.
// The method is called for each incoming message.
// Note that if the method returns `null`, no response will be sent to the client.
}

@OnTextMessage
Uni<ResponseMessage> processAsync(Message m) {
// Process the incoming message and send a response to the client.
// The method is called for each incoming message.
// Note that if the method returns `null`, no response will be sent to the client. The method completes when the returned Uni emits its item.
}

@OnTextMessage
Multi<ResponseMessage> stream(Message m) {
// Process the incoming message and send multiple responses to the client.
// The method is called for each incoming message.
// The method completes when the returned Multi emits its completion signal.
// The method cannot return `null` (but an empty multi if no response must be sent)
}

Uni および Multi を返すメソッドは、ノンブロッキングとみなされます。 さらに、Quarkus は返された Multi/Uni を自動的にサブスクライブし、完了、失敗、またはキャンセルされるまで、発行された項目を書き込みます。 失敗またはキャンセルにより接続が終了します。

5.6.4. ストリーム

WebSocket エンドポイントは、個々のメッセージに加えて、メッセージのストリームも処理できます。 この場合、メソッドは Multi<X> をパラメーターとして受け取ります。 X の各インスタンスは、上記と同じルールを使用してデシリアライズされます。

Multi を受け取るメソッドは、別の Multi または void を返すことができます。 メソッドが Multi を返す場合、受信する multi をサブスクライブする必要はありません。

@OnTextMessage
public Multi<ChatMessage> stream(Multi<ChatMessage> incoming) {
    return incoming.log();
}

このアプローチにより、双方向のストリーミングが可能になります。

メソッドが void を返し、 Multi を返さない場合、コードは受信する Multi をサブスクライブする必要があります。 そうしないと、データが消費されず、接続が閉じられません。

@OnTextMessage
public void stream(Multi<ChatMessage> incoming) {
    incoming.subscribe().with(item -> log(item));
}

また、 stream メソッドは Multi が完了する前に完了することに注意してください。

受信した Multi のサブスクライブに関する詳細は、Uni または Multi をサブスクライブするタイミング を参照してください。

5.6.5. 返信をスキップする

メソッドがクライアントに書き込まれるメッセージを生成することを意図している場合、 null を発行できます。 null を発行すると、クライアントにレスポンスが送信されないことを意味し、必要なときにレスポンスをスキップできるようになります。

5.6.6. JsonObject および JsonArray

Vert.x の JsonObject および JsonArray インスタンスは、シリアライゼーションおよびデシリアライゼーションのメカニズムをバイパスします。 メッセージはテキストメッセージとして送信されます。

5.6.7. OnOpen および OnClose メソッド

クライアントが接続または切断したときに、WebSocket エンドポイントに通知することもできます。

これは、メソッドに @OnOpen または @OnClose アノテーションを付けることで行われます。

@OnOpen(broadcast = true)
public ChatMessage onOpen() {
    return new ChatMessage(MessageType.USER_JOINED, connection.pathParam("username"), null);
}

@Inject WebSocketConnection connection;

@OnClose
public void onClose() {
    ChatMessage departure = new ChatMessage(MessageType.USER_LEFT, connection.pathParam("username"), null);
    connection.broadcast().sendTextAndAwait(departure);
}

@OnOpen はクライアント接続時にトリガーされ、 @OnClose は切断時に呼び出されます。

これらのメソッドは、セッションスコープWebSocketConnection Bean にアクセスできます。

5.6.8. パラメーター

@OnOpen および @OnClose アノテーションが付けられたメソッドは、次のパラメーターを受け入れることができます。

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • @PathParam アノテーションが付けられた String パラメーター

@OnClose アノテーションが付与されたエンドポイントメソッドは、接続を閉じる理由を示す io.quarkus.websockets.next.CloseReason パラメーターも受け入れる場合があります。

5.6.9. サポートされている戻り値のタイプ

@OnOpen メソッドと @OnClose メソッドは、異なる戻り値のタイプをサポートします。

@OnOpen メソッドには、 @On[Text|Binary]Message と同じルールが適用されます。 したがって、 @OnOpen アノテーションが付与されたメソッドは、接続後すぐにクライアントにメッセージを送信できます。 @OnOpen メソッドでサポートされている戻り値のタイプは次のとおりです。

  • void: 接続されたクライアントに明示的なメッセージが返されないブロッキングメソッドを示します。

  • Uni<Void>: 返された Uni の完了が処理の終了を意味するノンブロッキングメソッドを示します。クライアントにメッセージは返されません。

  • X タイプのオブジェクト: 返されたオブジェクトがシリアライズされてクライアントに送り返されるブロッキングメソッドを表します。

  • Uni<X>: null 以外の Uni によって発行された項目がクライアントに送信されるノンブロッキングメソッドを指定します。

  • Multi<X>: null 以外の Multi によって発行された項目が完了またはキャンセルされるまでクライアントに順番に送信されるノンブロッキングメソッドを示します。

  • Unit を返す Kotlin の suspend 関数: 明示的なメッセージがクライアントに返されないノンブロッキングメソッドを示します。

  • X を返す Kotlin の suspend 関数: 返された項目がクライアントに送信されるノンブロッキングメソッドを指定します。

クライアントに送信される項目は、 Stringio.vertx.core.json.JsonObjectio.vertx.core.json.JsonArrayio.vertx.core.buffer.Buffer、および byte[] タイプを除いて、シリアライズ されます。 Multi の場合、Quarkus は返された Multi をサブスクライブし、項目が発行されると WebSocket に書き込みます。 StringJsonObjectJsonArray はテキストメッセージとして送信されます。 Buffers とバイト配列はバイナリーメッセージとして送信されます。

@OnClose メソッドの場合、サポートされる戻り値のタイプは次のとおりです。

  • void: メソッドはブロッキングであるとみなされます。

  • Uni<Void>: メソッドはノンブロッキングであるとみなされます。

  • Unit を返す Kotlin の suspend 関数: このメソッドはノンブロッキングとみなされます。

サーバーエンドポイントで宣言された @OnClose メソッドは、オブジェクトを返すことによって接続されたクライアントに項目を送信できません。 WebSocketConnection オブジェクトを使用してのみ、他のクライアントにメッセージを送信できます。

5.7. エラー処理

エラーが発生したときに WebSocket エンドポイントに通知することもできます。 @io.quarkus.websockets.next.OnError アノテーションが付与された WebSocket エンドポイントメソッドは、エンドポイントコールバックがランタイムエラーをスローしたとき、または変換エラーが発生したとき、 あるいは返された io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi が失敗した場合に呼び出されます。

メソッドは、error パラメーター、つまり java.lang.Throwable から割り当て可能なパラメーターを 1 つだけ受け入れる必要があります。 このメソッドは次のパラメーターも受け入れます。

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • @PathParam アノテーションが付けられた String パラメーター

エンドポイントは、 @io.quarkus.websockets.next.OnError アノテーションが付与された複数のメソッドを宣言できます。 ただし、各メソッドは異なるエラーパラメーターを宣言する必要があります。 実際の例外の最も具体的なスーパータイプを宣言するメソッドが選択されます。

@io.quarkus.websockets.next.OnError アノテーションは、グローバルエラーハンドラー、つまり WebSocket エンドポイントで宣言されていないメソッドを宣言するためにも使用できます。このようなメソッドは、 @PathParam パラメーターを受け入れない場合があります。エンドポイントで宣言されたエラーハンドラーは、グローバルエラーハンドラーよりも優先されます。

エラーが発生しても、エラーハンドラーが失敗を処理できない場合、Quarkus は quarkus.websockets-next.server.unhandled-failure-strategy で指定されたストラテジーを使用します。 サーバーエンドポイントの場合、エラーメッセージがログに記録され、接続はデフォルトで閉じられます。 クライアントエンドポイントの場合、エラーメッセージはデフォルトでログに記録されます。

5.8. シリアライズとデシリアライズ

WebSocket Next エクステンションは、メッセージの自動シリアライゼーションとデシリアライゼーションをサポートします。

StringJsonObjectJsonArrayBuffer、および byte[] タイプのオブジェクトはそのまま送信され、シリアライゼーションとデシリアライゼーションをバイパスします。 コーデックが指定されていない場合、シリアライゼーションとデシリアライゼーションによってメッセージは JSON から、または JSON に自動的に変換されます。

シリアライゼーションとデシリアライゼーションをカスタマイズする必要がある場合は、カスタムコーデックを提供できます。

5.8.1. カスタムコーデック

カスタムコーデックを実装するには、以下を実装する CDI Bean を提供する必要があります。

  • バイナリーメッセージ用の io.quarkus.websockets.next.BinaryMessageCodec

  • テキストメッセージの io.quarkus.websockets.next.TextMessageCodec

次の例は、 Item クラスのカスタムコーデックを実装する方法を示しています。

@Singleton
public class ItemBinaryMessageCodec implements BinaryMessageCodec<Item> {

    @Override
    public boolean supports(Type type) {
        // Allows selecting the right codec for the right type
        return type.equals(Item.class);
    }

    @Override
    public Buffer encode(Item value) {
        // Serialization
        return Buffer.buffer(value.toString());
    }

    @Override
    public Item decode(Type type, Buffer value) {
        // Deserialization
        return new Item(value.toString());
    }
}

OnTextMessage メソッドと OnBinaryMessage メソッドでは、どのコーデックを使用するかを明示的に指定することもできます。

@OnTextMessage(codec = MyInputCodec.class) (1)
Item find(Item item) {
        //....
}
  1. メッセージのデシリアライゼーションとシリアライゼーションの両方に使用するコーデックを指定します。

シリアライゼーションとデシリアライゼーションで異なるコーデックを使用する必要がある場合は、シリアライゼーションとデシリアライゼーションに使用するコーデックを個別に指定できます。

@OnTextMessage(
        codec = MyInputCodec.class, (1)
        outputCodec = MyOutputCodec.class (2)
Item find(Item item) {
        //....
}
  1. 受信メッセージのデシリアライズに使用するコーデックを指定します

  2. 送信メッセージのシリアライゼーションに使用するコーデックを指定します。

5.9. Ping/Pong メッセージ

ping メッセージ は、キープアライブとして、またはリモートエンドポイントを確認するために使用できます。 pong メッセージ は、ping メッセージへのレスポンスとして送信され、同一のペイロードを持つ必要があります。

5.9.1. ping メッセージの送信

ping メッセージはオプションであり、デフォルトでは送信されません。ただし、サーバーおよびクライアントのエンドポイントは、一定の間隔で ping メッセージを自動的に送信するように設定できます。

quarkus.websockets-next.server.auto-ping-interval=2 (1)
quarkus.websockets-next.client.auto-ping-interval=10 (2)
1 サーバーから接続された各クライアントに 2 秒ごとに ping メッセージを送信します。
2 接続されているすべてのクライアントインスタンスからリモートサーバーに 10 秒ごとに ping メッセージを送信します。

サーバーとクライアントは、 WebSocketConnection または WebSocketClientConnection を使用して、いつでもプログラムで ping メッセージを送信できます。 ノンブロッキングバリアント (Sender#sendPing(Buffer)) とブロッキングバリアント (Sender#sendPingAndAwait(Buffer)) があります。

5.9.2. pong メッセージの送信

サーバーおよびクライアントのエンドポイントは、ping メッセージのアプリケーションデータを使用して、リモート側から送信された ping メッセージに対し、常に対応する pong メッセージでレスポンスします。 この動作は組み込まれており、追加のコードや設定は必要ありません。

サーバーとクライアントは、 WebSocketConnection または WebSocketClientConnection を使用して、一方向のハートビートとして機能する可能性のある非要求 pong メッセージを送信できます。ノンブロッキングバリアント (Sender#sendPong(Buffer)) とブロッキングバリアント (Sender#sendPongAndAwait(Buffer)) があります。

5.9.3. ping/pong メッセージの処理

ping メッセージは自動的に処理され、pong メッセージはレスポンスを必要としないため、WebSocket プロトコルに準拠するためにこれらのメッセージのハンドラーを記述する必要はありません。 ただし、エンドポイントで ping または pong メッセージがいつ受信されたかを知ることが役立つ場合があります。

@OnPingMessage および @OnPongMessage アノテーションを使用して、リモート側から送信された ping または pong メッセージを使用するコールバックを定義できます。 エンドポイントは、最大 1 つの @OnPingMessage コールバックと最大 1 つの @OnPongMessage コールバックを宣言できます。 コールバックメソッドは、 void または Uni<Void> を返す必要 (または Unit を返す Kotlin の suspend 関数である必要) があり、 Buffer タイプの単一のパラメーターを受け入れる必要があります。

@OnPingMessage
void ping(Buffer data) {
    // an incoming ping that will automatically receive a pong
}

@OnPongMessage
void pong(Buffer data) {
    // an incoming pong in response to the last ping sent
}

5.10. 受信処理モード

WebSocket エンドポイントは、それぞれ @WebSocket#inboundProcessingMode()@WebSocketClient.inboundProcessingMode() を使用して、特定の接続の受信イベントを処理するために使用されるモードを定義できます。 受信イベントは、メッセージ (テキスト、バイナリー、pong)、接続の開始、接続の終了を表すことができます。 デフォルトでは、イベントは順番に処理され、順序が保証されます。 つまり、エンドポイントがイベント AB を (この特定の順序で) 受信した場合、イベント A のコールバックが完了した後にイベント B のコールバックが呼び出されます。 ただし、状況によっては、順序の保証はなく、同時実行の制限もない状態でイベントを同時に処理することが望ましい場合があります。 このような場合には、 InboundProcessingMode#CONCURRENT を使用する必要があります。

6. サーバー API

6.1. HTTP サーバーの設定

このエクステンションは、メイン の HTTP サーバーを再利用します。

したがって、WebSocket サーバーの設定は quarkus.http. 設定セクションで行われます。

アプリケーション内で設定された WebSocket パスは、 quarkus.http.root (デフォルトは /) で定義されたルートパスと連結されます。 この連結により、WebSocket エンドポイントがアプリケーションの URL 構造内に適切に配置されるようになります。

詳細は、HTTP ガイド を参照してください。

6.2. sub-websocket エンドポイント

@WebSocket エンドポイントは、静的なネストされたクラスをカプセル化できます。これらのクラスも @WebSocket アノテーションが付与され、sub-websocket を表します。 これらの sub-websocket のパスは、外側のクラスとネストされたクラスからのパスを連結したものになります。 この結果得られるパスは、HTTP URL の規則に従って正規化されます。

sub-websocket は、外側のクラスとネストされたクラスの両方の @WebSocket アノテーションで宣言されたパスパラメーターへのアクセスを継承します。 次の例では、外側のクラス内の consumePrimary メソッドは version パラメーターにアクセスできます。 一方、ネストされたクラス内の consumeNested メソッドは、 version パラメーターと id パラメーターの両方にアクセスできます。

@WebSocket(path = "/ws/v{version}")
public class MyPrimaryWebSocket {

    @OnTextMessage
    void consumePrimary(String s)    { ... }

    @WebSocket(path = "/products/{id}")
    public static class MyNestedWebSocket {

      @OnTextMessage
      void consumeNested(String s)    { ... }

    }
}

6.3. WebSocket 接続

io.quarkus.websockets.next.WebSocketConnection オブジェクトは WebSocket 接続を表します。 Quarkus は、このインターフェイスを実装し、 WebSocket エンドポイントに注入して、接続されたクライアントと対話するために使用できる @SessionScoped CDI Bean を提供します。

@OnOpen@OnTextMessage@OnBinaryMessage、および @OnClose アノテーションが付与されたメソッドは、注入された WebSocketConnection オブジェクトにアクセスできます。

@Inject WebSocketConnection connection;
これらのメソッド以外では、 WebSocketConnection オブジェクトは利用できないことに注意してください。ただし、開いているすべての接続をリスト表示 できます.。

接続を使用して、クライアントにメッセージを送信したり、パスパラメーターにアクセスしたり、接続されているすべてのクライアントにメッセージをブロードキャストしたりできます。

// Send a message:
connection.sendTextAndAwait("Hello!");

// Broadcast messages:
connection.broadcast().sendTextAndAwait(departure);

// Access path parameters:
String param = connection.pathParam("foo");

WebSocketConnection は、メッセージを送信するためのブロッキングメソッドとノンブロッキングメソッドの両方のバリアントを提供します。

  • sendTextAndAwait(String message): テキストメッセージをクライアントに送信し、メッセージが送信されるのを待ちます。これはブロッキングであり、エグゼキュータースレッドからのみ呼び出す必要があります。

  • sendText(String message): テキストメッセージをクライアントに送信します。 Uni を返します。これはノンブロッキングです。メッセージを送信するには、返された Uni をユーザーまたは Quarkus がサブスクライブしていることを確認してください。 Quarkus によって呼び出されたメソッド (Quarkus REST、Quarkus WebSocket Next、Quarkus Messaging など) から Uni を返すと、それをサブスクライブしてメッセージを送信します。 以下に例を示します。

@POST
public Uni<Void> send() {
    return connection.sendText("Hello!"); // Quarkus automatically subscribes to the returned Uni and sends the message.
}

Uni への登録に関する詳細は、Uni または Multi をサブスクライブするタイミング を参照してください。

6.3.1. 開いている接続をリスト表示する

開いているすべての接続をリスト表示することもできます。 Quarkus は、接続にアクセスするための便利なメソッドを宣言する io.quarkus.websockets.next.OpenConnections タイプの CDI Bean を提供します。

import io.quarkus.logging.Log;
import io.quarkus.websockets.next.OpenConnections;

class MyBean {

  @Inject
  OpenConnections connections;

  void logAllOpenConnections() {
     Log.infof("Open connections: %s", connections.listAll()); (1)
  }
}
1 OpenConnections#listAll() は、指定された時点で開いているすべての接続のイミュータブルなスナップショットを返します。

他にも便利な方法があります。 たとえば、 OpenConnections#findByEndpointId(String) を使用すると、特定のエンドポイントの接続を簡単に見つけることができます。

6.3.2. ユーザーデータ

任意のユーザーデータを特定の接続に関連付けることも可能です。 WebSocketConnection#userData() メソッドによって取得される io.quarkus.websockets.next.UserData オブジェクトは、接続に関連付けられたミュータブルなユーザーデータを表します。

import io.quarkus.websockets.next.WebSocketConnection;
import io.quarkus.websockets.next.UserData.TypedKey;

@WebSocket(path = "/endpoint/{username}")
class MyEndpoint {

  @Inject
  CoolService service;

  @OnOpen
  void open(WebSocketConnection connection) {
     connection.userData().put(TypedKey.forBoolean("isCool"), service.isCool(connection.pathParam("username"))); (1)
  }

  @OnTextMessage
  String process(String message) {
     if (connection.userData().get(TypedKey.forBoolean("isCool"))) { (2)
        return "Cool message processed!";
     } else {
        return "Message processed!";
     }
  }
}
1 CoolService#isCool() は、現在の接続に関連付けられている Boolean を返します。
2 TypedKey.forBoolean("isCool") は、接続の作成時に保存されたデータを取得するために使用されるキーです。

6.3.3. CDI イベント

Quarkus は、新しい接続が開かれると、修飾子 @io.quarkus.websockets.next.Open を持つ io.quarkus.websockets.next.WebSocketConnection タイプの CDI イベントを非同期的に起動します。 さらに、接続が閉じられると、修飾子 @io.quarkus.websockets.next.Closed を持つ WebSocketConnection タイプの CDI イベントが非同期的に起動します。

import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketConnection;

class MyBean {

  void connectionOpened(@ObservesAsync @Open WebSocketConnection connection) { (1)
     // This observer method is called when a connection is opened...
  }
}
1 非同期オブザーバーメソッドは、デフォルトのブロッキングエグゼキューターサービスを使用して実行されます。

6.4. セキュリティー

セキュリティー機能は、Quarkus Security エクステンションによって提供されます。 任意の アイデンティティープロバイダー を使用して、最初の HTTP リクエストの認証情報を SecurityIdentity インスタンスに変換できます。 次に、 SecurityIdentity が Websocket 接続に関連付けられます。 認可オプションについては、次のセクションで説明します。

OpenID Connect エクステンション (quarkus-oidc) が使用され、トークンの有効期限が切れると、Quarkus は自動的に接続を閉じます。

6.4.1. セキュア HTTP アップグレード

標準のセキュリティーアノテーションがエンドポイントクラスに配置されるか、HTTP セキュリティーポリシーが定義されている場合、HTTP アップグレードは保護されます。 HTTP アップグレードをセキュリティーで保護する利点は、処理が少なくなり、認可が早期に 1 回だけ実行されることです。 エラー時にアクションを実行する必要がある場合 (セキュアな WebSocket エンドポイントコールバックメソッド 参照) や、ペイロードに基づいてセキュリティチェックを行う必要がある場合 (権限チェッカーでサーバーのエンドポイントを保護する を参照) を除き、常に HTTP アップグレードセキュリティーを優先する必要があります。

標準のセキュリティーアノテーションを使用して HTTP アップグレードを保護する
package io.quarkus.websockets.next.test.security;

import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@Authenticated (1)
@WebSocket(path = "/end")
public class Endpoint {

    @Inject
    SecurityIdentity currentIdentity;

    @OnOpen
    String open() {
        return "ready";
    }

    @OnTextMessage
    String echo(String message) {
        return message;
    }
}
1 匿名ユーザーの場合、最初の HTTP ハンドシェイクは 401 ステータスで終了します。 quarkus.websockets-next.server.security.auth-failure-redirect-url 設定プロパティーを使用して、認可失敗時にハンドシェイクリクエストをリダイレクトすることもできます。
HTTP アップグレードは、エンドポイントクラスで @WebSocket アノテーションの隣でセキュリティーアノテーションが宣言されている場合にのみ保護されます。 エンドポイント Bean にセキュリティーアノテーションを配置しても、Bean メソッドは保護されず、HTTP アップグレードのみが保護されます。 エンドポイントが意図したとおりに保護されていることを常に確認する必要があります。
HTTP セキュリティーポリシーを使用して HTTP アップグレードを保護する
quarkus.http.auth.permission.http-upgrade.paths=/end
quarkus.http.auth.permission.http-upgrade.policy=authenticated

6.4.2. セキュアな WebSocket エンドポイントコールバックメソッド

WebSocket エンドポイントのコールバックメソッドは、 io.quarkus.security.Authenticatedjakarta.annotation.security.RolesAllowed などのセキュリティーアノテーション、および サポート対象セキュリティーアノテーション ドキュメントに記載されているその他のアノテーションを使用して保護できます。

例:

package io.quarkus.websockets.next.test.security;

import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@WebSocket(path = "/end")
public class Endpoint {

    @Inject
    SecurityIdentity currentIdentity;

    @OnOpen
    String open() {
        return "ready";
    }

    @RolesAllowed("admin")
    @OnTextMessage
    String echo(String message) { (1)
        return message;
    }

    @OnError
    String error(ForbiddenException t) { (2)
        return "forbidden:" + currentIdentity.getPrincipal().getName();
    }
}
1 エコーコールバックメソッドは、現在のセキュリティーアイデンティティーに admin ロールがある場合にのみ呼び出すことができます。
2 認可に失敗した場合はエラーハンドラーが呼び出されます。

6.4.3. 権限チェッカーでサーバーのエンドポイントを保護する

WebSocket エンドポイントは、パーミッションチェッカー を使用して保護できます。 個々のエンドポイントメソッドではなく、セキュア HTTP アップグレード を推奨します。以下に例を示します。

セキュアな HTTP アップグレードを備えた WebSocket エンドポイントの例
package io.quarkus.websockets.next.test.security;

import io.quarkus.security.PermissionsAllowed;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@PermissionsAllowed("product:premium")
@WebSocket(path = "/product/premium")
public class PremiumProductEndpoint {

    @OnTextMessage
    PremiumProduct getPremiumProduct(int productId) {
        return new PremiumProduct(productId);
    }

}
HTTP アップグレードを承認する権限チェッカーの例
package io.quarkus.websockets.next.test.security;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.PermissionChecker;
import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils;
import io.vertx.ext.web.RoutingContext;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class PermissionChecker {

    @PermissionChecker("product:premium")
    public boolean canGetPremiumProduct(SecurityIdentity securityIdentity) { (1)
        String username = currentIdentity.getPrincipal().getName();

        RoutingContext routingContext = HttpSecurityUtils.getRoutingContextAttribute(securityIdentity);
        String initialHttpUpgradePath = routingContext == null ? null : routingContext.normalizedPath();
        if (!isUserAllowedToAccessPath(initialHttpUpgradePath, username)) {
            return false;
        }

        return isPremiumCustomer(username);
    }

}
1 HTTP アップグレードを承認する権限チェッカーは、メソッドパラメーター SecurityIdentity を 1 つだけ宣言する必要があります。

すべてのメッセージに対してセキュリティーチェックを実行することもできます。たとえば、メッセージペイロードには次のようにアクセスできます。

package io.quarkus.websockets.next.test.security;

import io.quarkus.security.PermissionChecker;
import io.quarkus.security.PermissionsAllowed;
import jakarta.inject.Inject;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@WebSocket(path = "/product")
public class ProductEndpoint {

    private record Product(int id, String name) {}

    @Inject
    SecurityIdentity currentIdentity;

    @PermissionsAllowed("product:get")
    @OnTextMessage
    Product getProduct(int productId) { (1)
        return new Product(productId, "Product " + productId);
    }

    @OnError
    String error(ForbiddenException t) { (2)
        return "forbidden:" + currentIdentity.getPrincipal().getName();
    }

    @PermissionChecker("product:get")
    boolean canGetProduct(int productId) {
        String username = currentIdentity.getPrincipal().getName();
        return currentIdentity.hasRole("admin") || canUserGetProduct(productId, username);
    }
}
1 getProduct コールバックメソッドは、現在のセキュリティーアイデンティティーに admin ロールがある場合、またはユーザーが製品の詳細を取得することを許可されている場合にのみ、呼び出すことができます。
2 認可に失敗した場合はエラーハンドラーが呼び出されます。

6.4.4. ベアラートークン認証

OIDC ベアラートークン認証 では、最初の HTTP ハンドシェイク中にベアラートークンが Authorization ヘッダーで渡されることを期待します。 WebSockets Next Client および Vert.x WebSocketClient などの Java WebSocket クライアントは、WebSocket オープニングハンドシェイクにカスタムヘッダーを追加することをサポートしています。 ただし、 WebSockets API に準拠する JavaScript クライアントは、カスタムヘッダーの追加をサポートしていません。 したがって、JavaScript ベースの WebSocket クライアントでは、カスタム Authorization ヘッダーを使用してベアラーアクセストークンを渡すことはできません。 JavaScript WebSocket クライアントでは、サブプロトコルをネゴシエートするための HTTP Sec-WebSocket-Protocol リクエストヘッダーのみを設定できます。 絶対に必要な場合は、 WebSockets API の制限を回避するために、 Sec-WebSocket-Protocol ヘッダーをカスタムヘッダーのキャリアーとして使用できます。 以下は、 Authorization ヘッダーをサブプロトコル値として伝播する JavaScript クライアントの例です。

const token = getBearerToken()
const quarkusHeaderProtocol = encodeURIComponent("quarkus-http-upgrade#Authorization#Bearer " + token) (1)
const socket = new WebSocket("wss://" + location.host + "/chat/" + username, ["bearer-token-carrier", quarkusHeaderProtocol]) (2)
1 Quarkus Header サブプロトコルの想定される形式は quarkus-http-upgrade#header-name#header-value です。 エンコードの問題を回避するために、サブプロトコル値を URI コンポーネントとしてエンコードすることを忘れないでください。
2 クライアントでサポートされている 2 つのサブプロトコル (選択したサブプロトコルと Quarkus HTTP アップグレードサブプロトコル) を指定します。

WebSocket サーバーがサブプロトコルとして渡された Authorization を受け入れるには、次の操作を行う必要があります。

  • サポートされているサブプロトコルを使用して、WebSocket サーバーを設定します。WebSocket クライアントが HTTP Sec-WebSocket-Protocol リクエストヘッダーでサポートされているサブプロトコルのリストを提供する場合、WebSocket サーバーはそれらのいずれかを使用してコンテンツを提供することに同意する必要があります。

  • 開いている WebSocket ハンドシェイクリクエストヘッダーへの Quarkus HTTP アップグレードサブプロトコルマッピングを有効にします。

quarkus.websockets-next.server.supported-subprotocols=bearer-token-carrier
quarkus.websockets-next.server.propagate-subprotocol-headers=true

WebSocket セキュリティーモデルはオリジンベースであり、ヘッダーまたは Cookie を使用したクライアント側認証用に設計されていません。 たとえば、Web ブラウザーは、WebSocket ハンドシェイクリクエストの開始時に、同一オリジンポリシーを強制しません。 WebSocket ハンドシェイクリクエストの開始時に、ベアラーアクセストークンを使用する予定の場合は、セキュリティーリスクを最小限に抑えるために、以下に示す追加のセキュリティー対策に従うことを強く推奨します。

  • CORS フィルター を使用して、サポートされている Origin を信頼できる Origin のみに制限します。

  • TLS 経由で暗号化された HTTP 接続を強制するには、 wss プロトコルを使用します。

  • ランダムトークンを HTML ページに提供するカスタム WebSocket チケットシステムを使用します。この HTML ページは、ハンドシェイクリクエストの開始時にクエリーパラメーターとしてこのトークンを提供する必要がある JavaScript WebSocket クライアントをホストします。

6.5. HTTP アップグレードを検査および/または拒否する

HTTP アップグレードを検査するには、 io.quarkus.websockets.next.HttpUpgradeCheck インターフェイスを実装する CDI Bean を提供する必要があります。 Quarkus は、WebSocket 接続にアップグレードする必要があるすべての HTTP リクエストに対して HttpUpgradeCheck#perform メソッドを呼び出します。 このメソッド内では、任意のビジネスロジックを実行したり、HTTP アップグレードを拒否したりできます。

HttpUpgradeCheck の例
package io.quarkus.websockets.next.test;

import io.quarkus.websockets.next.HttpUpgradeCheck;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped (1)
public class ExampleHttpUpgradeCheck implements HttpUpgradeCheck {

    @Override
    public Uni<CheckResult> perform(HttpUpgradeContext ctx) {
        if (rejectUpgrade(ctx)) {
            return CheckResult.rejectUpgrade(400); (2)
        }
        return CheckResult.permitUpgrade();
    }

    private boolean rejectUpgrade(HttpUpgradeContext ctx) {
        var headers = ctx.httpRequest().headers();
        // implement your business logic in here
    }
}
1 HttpUpgradeCheck インターフェイスを実装する CDI Bean は、 @ApplicationScoped@Singleton、または @Dependent Bean のいずれかになりますが、 @RequestScoped Bean になることはできません。
2 HTTP アップグレードを拒否します。最初の HTTP ハンドシェイクは、400 Bad Request レスポンスステータスコードで終了します。
HttpUpgradeCheck#appliesTo メソッドを使用して、 HttpUpgradeCheck を適用する WebSocket エンドポイントを選択できます。

6.6. TLS

このエクステンションは、メイン の HTTP サーバーを再利用するという事実の直接的な結果として、関連するすべてのサーバー設定が適用されます。詳細は、HTTP ガイド を参照してください。

6.7. Hibernate マルチテナンシー

HTTP アップグレード後、 RoutingContext は使用できません。ただし、 WebSocketConnection を注入して、最初の HTTP リクエストのヘッダーにアクセスすることは可能です。

カスタムの TenantResolver を使用し、REST/HTTP と WebSocket を組み合わせる場合、コードは次のようになります。

@RequestScoped
@PersistenceUnitExtension
public class CustomTenantResolver implements TenantResolver {

    @Inject
    RoutingContext context;
    @Inject
    WebSocketConnection connection;

    @Override
    public String getDefaultTenantId() {
        return "public";
    }

    @Override
    public String resolveTenantId() {
        String schema;
        try {
            //Handle WebSocket
            schema = connection.handshakeRequest().header("schema");
        } catch ( ContextNotActiveException e) {
            // Handle REST/HTTP
            schema = context.request().getHeader( "schema" );
        }

        if ( schema == null || schema.equalsIgnoreCase( "public" ) ) {
            return "public";
        }

        return schema;
    }
}

Hibernate マルチテナンシーの詳細は、 hibernate ドキュメント を参照してください。

7. クライアント API

7.1. クライアントコネクター

コネクターを使用すると、メッセージの消費と送信に使用されるクライアントエンドポイントによってサポートされる新しいクライアント接続を設定して開くことができます。 Quarkus は、Bean タイプ io.quarkus.websockets.next.WebSocketConnector<CLIENT> の CDI Bean および他の Bean に注入できるデフォルトの修飾子を提供します。 注入ポイントの実際のタイプ引数は、クライアントエンドポイントを決定するために使用されます。 このタイプはビルド時に検証され、クライアントエンドポイントを表していない場合、ビルドは失敗します。

次のクライアントエンドポイントを考えてみましょう。

クライアントエンドポイント
@WebSocketClient(path = "/endpoint/{name}")
public class ClientEndpoint {

    @OnTextMessage
    void onMessage(@PathParam String name, String message, WebSocketClientConnection connection) {
        // ...
    }
}

このクライアントエンドポイントのコネクターは次のように使用されます。

コネクター
@Singleton
public class MyBean {

    @ConfigProperty(name = "endpoint.uri")
    URI myUri;

    @Inject
    WebSocketConnector<ClientEndpoint> connector; (1)

    void openAndSendMessage() {
        WebSocketClientConnection connection = connector
            .baseUri(uri) (2)
            .pathParam("name", "Roxanne") (3)
            .connectAndAwait();
        connection.sendTextAndAwait("Hi!"); (4)
    }
}
1 ClientEndpoint のコネクターを注入します。
2 ベース URI が指定されていない場合は、設定から値を取得しようとします。キーは、クライアント ID と .base-uri 接尾辞で構成されます。
3 パスパラメーター値を設定します。クライアントエンドポイントパスに指定された名前のパラメーターが含まれていない場合は、 IllegalArgumentException がスローされます。
4 必要に応じて、接続を使用してメッセージを送信します。
アプリケーションが、存在しないエンドポイントのコネクターを注入しようとすると、エラーがスローされます。

コネクターはスレッドセーフではないため、同時に使用しないでください。 また、コネクターの再利用もしないでください。 連続して複数の接続を作成する必要がある場合は、 Instance#get() を使用してプログラムで新しいコネクターインスタンスを取得する必要があります。

import jakarta.enterprise.inject.Instance;

@Singleton
public class MyBean {

    @Inject
    Instance<WebSocketConnector<MyEndpoint>> connector;

    void connect() {
        var connection1 = connector.get().baseUri(uri)
                .addHeader("Foo", "alpha")
                .connectAndAwait();
        var connection2 = connector.get().baseUri(uri)
                .addHeader("Foo", "bravo")
                .connectAndAwait();
    }
}

7.1.1. 基本コネクター

アプリケーション開発者がクライアントエンドポイントとコネクターの組み合わせを必要としない場合は、基本コネクター を使用できます。 基本コネクターは、クライアントエンドポイントを定義せずに接続を作成し、メッセージを消費/送信する簡単な方法です。

基本コネクター
@Singleton
public class MyBean {

    @Inject
    BasicWebSocketConnector connector; (1)

    void openAndConsume() {
        WebSocketClientConnection connection = connector
            .baseUri(uri) (2)
            .path("/ws") (3)
            .executionModel(ExecutionModel.NON_BLOCKING) (4)
            .onTextMessage((c, m) -> { (5)
               // ...
            })
            .connectAndAwait();
    }
}
1 コネクターを注入します。
2 ベース URI は常に設定する必要があります。
3 ベース URI に追加する必要がある追加パス。
4 コールバックハンドラーの実行モデルを設定します。デフォルトでは、コールバックは現在のスレッドをブロックする可能性があります。ただし、この場合、コールバックはイベントループで実行され、現在のスレッドをブロックしない可能性があります。
5 lambda は、サーバーから送信されるテキストメッセージごとに呼び出されます。

基本コネクターは低レベル API に近いため、上級ユーザー向けに予約されています。 ただし、他の低レベルの WebSocket クライアントとは異なり、これは引き続き CDI Bean であり、他の Bean に注入できます。 また、コールバックの実行モデルを設定する方法も提供し、Quarkus の他の部分との最適なインテグレーションを確保します。

コネクターはスレッドセーフではないため、同時に使用しないでください。 また、コネクターの再利用もしないでください。 連続して複数の接続を作成する必要がある場合は、 Instance#get() を使用してプログラムで新しいコネクターインスタンスを取得する必要があります。

import jakarta.enterprise.inject.Instance;

@Singleton
public class MyBean {

    @Inject
    Instance<BasicWebSocketConnector> connector;

    void connect() {
        var connection1 = connector.get().baseUri(uri)
                .addHeader("Foo", "alpha")
                .connectAndAwait();
        var connection2 = connector.get().baseUri(uri)
                .addHeader("Foo", "bravo")
                .connectAndAwait();
    }
}

7.2. WebSocket クライアント接続

io.quarkus.websockets.next.WebSocketClientConnection オブジェクトは WebSocket 接続を表します。 Quarkus は、このインターフェイスを実装し、 WebSocketClient エンドポイントに注入して、接続されたサーバーと対話するために使用できる @SessionScoped CDI Bean を提供します。

@OnOpen@OnTextMessage@OnBinaryMessage、および @OnClose アノテーションが付与されたメソッドは、注入された WebSocketClientConnection オブジェクトにアクセスできます。

@Inject WebSocketClientConnection connection;
これらのメソッド以外では、 WebSocketClientConnection オブジェクトは利用できないことに注意してください。ただし、開いているすべてのクライアント接続をリスト表示 することは可能です。

この接続を使用して、クライアントにメッセージを送信したり、パスパラメーターにアクセスしたりできます。

// Send a message:
connection.sendTextAndAwait("Hello!");

// Broadcast messages:
connection.broadcast().sendTextAndAwait(departure);

// Access path parameters:
String param = connection.pathParam("foo");

WebSocketClientConnection は、メッセージを送信するためのブロッキングメソッドとノンブロッキングメソッドの両方のバリアントを提供します。

  • sendTextAndAwait(String message): テキストメッセージをクライアントに送信し、メッセージが送信されるのを待ちます。これはブロッキングであり、エグゼキュータースレッドからのみ呼び出す必要があります。

  • sendText(String message): テキストメッセージをクライアントに送信します。 Uni を返します。これはノンブロッキングです。メッセージを送信するには、返された Uni をユーザーまたは Quarkus がサブスクライブしていることを確認してください。 Quarkus によって呼び出されたメソッド (Quarkus REST、Quarkus WebSocket Next、Quarkus Messaging など) から Uni を返すと、それをサブスクライブしてメッセージを送信します。 以下に例を示します。

@POST
public Uni<Void> send() {
    return connection.sendText("Hello!"); // Quarkus automatically subscribes to the returned Uni and sends the message.
}

7.2.1. 開いているクライアント接続をリスト表示する

開いているすべての接続をリスト表示することもできます。 Quarkus は、接続にアクセスするための便利なメソッドを宣言する io.quarkus.websockets.next.OpenClientConnections タイプの CDI Bean を提供します。

import io.quarkus.logging.Log;
import io.quarkus.websockets.next.OpenClientConnections;

class MyBean {

  @Inject
  OpenClientConnections connections;

  void logAllOpenClinetConnections() {
     Log.infof("Open client connections: %s", connections.listAll()); (1)
  }
}
1 OpenClientConnections#listAll() は、指定された時点で開いているすべての接続のイミュータブルなスナップショットを返します。

他にも便利な方法があります。 たとえば、 OpenClientConnections#findByClientId(String) を使用すると、特定のエンドポイントの接続を簡単に見つけることができます。

7.2.2. ユーザーデータ

任意のユーザーデータを特定の接続に関連付けることも可能です。 WebSocketClientConnection#userData() メソッドによって取得される io.quarkus.websockets.next.UserData オブジェクトは、接続に関連付けられたミュータブルなユーザーデータを表します。

import io.quarkus.websockets.next.WebSocketClientConnection;
import io.quarkus.websockets.next.UserData.TypedKey;

@WebSocketClient(path = "/endpoint/{username}")
class MyEndpoint {

  @Inject
  CoolService service;

  @OnOpen
  void open(WebSocketClientConnection connection) {
     connection.userData().put(TypedKey.forBoolean("isCool"), service.isCool(connection.pathParam("username"))); (1)
  }

  @OnTextMessage
  String process(String message) {
     if (connection.userData().get(TypedKey.forBoolean("isCool"))) { (2)
        return "Cool message processed!";
     } else {
        return "Message processed!";
     }
  }
}
1 CoolService#isCool() は、現在の接続に関連付けられている Boolean を返します。
2 TypedKey.forBoolean("isCool") は、接続の作成時に保存されたデータを取得するために使用されるキーです。

7.2.3. CDI イベント

Quarkus は、新しい接続が開かれると、修飾子 @io.quarkus.websockets.next.Open を持つ io.quarkus.websockets.next.WebSocketConnection タイプの CDI イベントを非同期的に起動します。 さらに、接続が閉じられると、修飾子 @io.quarkus.websockets.next.Closed を持つ WebSocketClientConnection タイプの CDI イベントが非同期的に起動します。

import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketClientConnection;

class MyBean {

  void connectionOpened(@ObservesAsync @Open WebSocketClientConnection connection) { (1)
     // This observer method is called when a connection is opened...
  }
}
1 非同期オブザーバーメソッドは、デフォルトのブロッキングエグゼキューターサービスを使用して実行されます。

7.3. SSL/TLS の設定

TLS 接続を確立するには、TLS レジストリー を使用して、名前付き の設定を設定する必要があります。

quarkus.tls.my-ws-client.trust-store.p12.path=server-truststore.p12
quarkus.tls.my-ws-client.trust-store.p12.password=secret

quarkus.websockets-next.client.tls-configuration-name=my-ws-client # Reference the named configuration
WebSocket クライアントを使用する場合は、他の TLS 設定との競合を避けるために、名前付き 設定を使用する必要があります。 クライアントはデフォルトの TLS 設定を使用しません。

名前付き TLS 設定を行うと、TLS はデフォルトで有効になります。

8. トラフィックロギング

Quarkus は、デバッグの目的で送受信されたメッセージをログに記録できます。 サーバーのトラフィックロギングを有効にするには、 quarkus.websockets-next.server.traffic-logging.enabled 設定プロパティーを true に設定します。 クライアントのトラフィックロギングを有効にするには、 quarkus.websockets-next.client.traffic-logging.enabled 設定プロパティーを true に設定します。 テキストメッセージのペイロードも記録されます。 ただし、記録される文字数には制限があります。 デフォルトの制限は 100 ですが、 quarkus.websockets-next.server.traffic-logging.text-payload-limit および quarkus.websockets-next.client.traffic-logging.text-payload-limit 設定プロパティーをそれぞれ使用してこの制限を変更できます。

メッセージは、ロガー io.quarkus.websockets.next.traffic に対して DEBUG レベルが有効になっている場合にのみ記録されます。
サーバー設定の例
quarkus.websockets-next.server.traffic-logging.enabled=true (1)
quarkus.websockets-next.server.traffic-logging.text-payload-limit=50 (2)

quarkus.log.category."io.quarkus.websockets.next.traffic".level=DEBUG (3)
1 トラフィックロギングを有効にします。
2 ログに記録されるテキストメッセージペイロードの文字数を設定します。
3 ロガー io.quarkus.websockets.next.traffic に対して DEBUG レベルを有効にします。

9. Uni または Multi をサブスクライブするタイミング

UniMulti は遅延タイプです。つまり、サブスクライブされるまで処理を開始しません。

Uni または Multi を (パラメーターまたは呼び出したメソッドから) 取得する場合、それをサブスクライブするかどうかはコンテキストによって異なります。

  • Quarkus によって呼び出されるメソッド (Quarkus REST、Quarkus WebSocket Next、Quarkus Messaging など) で Uni または Multi を返すと、Quarkus はそれをサブスクライブし、 Multi によって発行された項目または Uni によって発行された項目を処理します。

@Incoming("...")
@Outgoing("...")
public Multi<String> process(Multi<String> input) {
    // No need to subscribe to the input Multi, the `process` method is called by Quarkus (Messaging).
    return input.map(String::toUpperCase);
}

@OnOpen@OnTextMessage@OnBinaryMessage、または @OnClose アノテーションが付与されたメソッドから Uni または Multi が返されると、Quarkus はそれを自動的にサブスクライブします。

  • Quarkus によって呼び出されるメソッドで Uni または Multi を返さない場合は、それをサブスクライブする必要があります。

@Incoming("...")
@Outgoing("...")
public void process(Multi<String> input) {
    input.map(String::toUpperCase)
        .subscribe().with(s -> log(s));
}

10. テレメトリー

OpenTelemetry エクステンションが存在する場合、開かれた WebSocket 接続と閉じられた WebSocket 接続のトレースがデフォルトで収集されます。 WebSocket トレースが必要ない場合は、次の例のようにトレースの収集を無効化できます。

quarkus.websockets-next.server.traces.enabled=false
quarkus.websockets-next.client.traces.enabled=false

Micrometer エクステンションが存在する場合、Quarkus はメッセージ、エラー、転送されたバイトのメトリクスを収集できます。 WebSocket メトリクスが必要な場合は、次の例のようにメトリクスを有効化できます。

quarkus.websockets-next.server.metrics.enabled=true
quarkus.websockets-next.client.metrics.enabled=true
BasicWebSocketConnector のテレメトリーは現在サポートされていません。

11. 設定リファレンス

ビルド時に固定された設定プロパティー。その他の設定プロパティーは、すべて実行時にオーバーライド可能です。

Configuration property

タイプ

デフォルト

Specifies the activation strategy for the CDI request context during endpoint callback invocation. By default, the request context is only activated if needed, i.e. if there is a bean with the given scope, or a bean annotated with a security annotation (such as @RolesAllowed), in the dependency tree of the endpoint.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_ACTIVATE_REQUEST_CONTEXT

Show more

autoThe context is only activated if needed., alwaysThe context is always activated.

autoThe context is only activated if needed.

Specifies the activation strategy for the CDI session context during endpoint callback invocation. By default, the session context is only activated if needed, i.e. if there is a bean with the given scope in the dependency tree of the endpoint.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_ACTIVATE_SESSION_CONTEXT

Show more

autoThe context is only activated if needed., alwaysThe context is always activated.

autoThe context is only activated if needed.

If enabled, the WebSocket opening handshake headers are enhanced with the 'Sec-WebSocket-Protocol' sub-protocol that match format 'quarkus-http-upgrade#header-name#header-value'. If the WebSocket client interface does not support setting headers to the WebSocket opening handshake, this is a way how to set authorization header required to authenticate user. The 'quarkus-http-upgrade' sub-protocol is removed and server selects from the sub-protocol one that is supported (don’t forget to configure the 'quarkus.websockets-next.server.supported-subprotocols' property). IMPORTANT: We strongly recommend to only enable this feature if the HTTP connection is encrypted via TLS, CORS origin check is enabled and custom WebSocket ticket system is in place. Please see the Quarkus WebSockets Next reference for more information.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_PROPAGATE_SUBPROTOCOL_HEADERS

Show more

ブーリアン

false

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_SUPPORTED_SUBPROTOCOLS

Show more

list of string

Compression Extensions for WebSocket are supported by default.

See also RFC 7692

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_PER_MESSAGE_COMPRESSION_SUPPORTED

Show more

ブーリアン

true

The compression level must be a value between 0 and 9. The default value is io.vertx.core.http.HttpServerOptions#DEFAULT_WEBSOCKET_COMPRESSION_LEVEL.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_COMPRESSION_LEVEL

Show more

int

The maximum size of a message in bytes. The default values is io.vertx.core.http.HttpServerOptions#DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_MAX_MESSAGE_SIZE

Show more

int

The maximum size of a frame in bytes. The default values is io.vertx.core.http.HttpServerOptions#DEFAULT_MAX_WEBSOCKET_FRAME_SIZE.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_MAX_FRAME_SIZE

Show more

int

The interval after which, when set, the server sends a ping message to a connected client automatically.

Ping messages are not sent automatically by default.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_AUTO_PING_INTERVAL

Show more

Duration 

The strategy used when an error occurs but no error handler can handle the failure.

By default, the error message is logged and the connection is closed when an unhandled failure occurs.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_UNHANDLED_FAILURE_STRATEGY

Show more

log-and-closeLog the error message and close the connection., closeClose the connection silently., logLog the error message., noopNo operation.

log-and-closeLog the error message and close the connection.

Quarkus redirects HTTP handshake request to this URL if an HTTP upgrade is rejected due to the authorization failure. This configuration property takes effect when you secure endpoint with a standard security annotation. For example, the HTTP upgrade is secured if an endpoint class is annotated with the @RolesAllowed annotation.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_SECURITY_AUTH_FAILURE_REDIRECT_URL

Show more

string

The limit of messages kept for a Dev UI connection. If less than zero then no messages are stored and sent to the Dev UI view.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_DEV_MODE_CONNECTION_MESSAGES_LIMIT

Show more

1000

If set to true then binary/text messages received/sent are logged if the DEBUG level is enabled for the logger io.quarkus.websockets.next.traffic.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_TRAFFIC_LOGGING_ENABLED

Show more

ブーリアン

false

The number of characters of a text message which will be logged if traffic logging is enabled. The payload of a binary message is never logged.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_TRAFFIC_LOGGING_TEXT_PAYLOAD_LIMIT

Show more

int

100

If collection of WebSocket traces is enabled. Only applicable when the OpenTelemetry extension is present.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_TRACES_ENABLED

Show more

ブーリアン

true

If collection of WebSocket metrics is enabled. Only applicable when the Micrometer extension is present.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_METRICS_ENABLED

Show more

ブーリアン

false

Compression Extensions for WebSocket are supported by default.

See also RFC 7692

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_OFFER_PER_MESSAGE_COMPRESSION

Show more

ブーリアン

false

The compression level must be a value between 0 and 9. The default value is io.vertx.core.http.HttpClientOptions#DEFAULT_WEBSOCKET_COMPRESSION_LEVEL.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_COMPRESSION_LEVEL

Show more

int

The maximum size of a message in bytes. The default values is io.vertx.core.http.HttpClientOptions#DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_MAX_MESSAGE_SIZE

Show more

int

The maximum size of a frame in bytes. The default values is io.vertx.core.http.HttpClientOptions#DEFAULT_MAX_WEBSOCKET_FRAME_SIZEX.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_MAX_FRAME_SIZE

Show more

int

The interval after which, when set, the client sends a ping message to a connected server automatically.

Ping messages are not sent automatically by default.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_AUTO_PING_INTERVAL

Show more

Duration 

The strategy used when an error occurs but no error handler can handle the failure.

By default, the error message is logged when an unhandled failure occurs.

Note that clients should not close the WebSocket connection arbitrarily. See also RFC-6455 section 7.3.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_UNHANDLED_FAILURE_STRATEGY

Show more

log-and-closeLog the error message and close the connection., closeClose the connection silently., logLog the error message., noopNo operation.

logLog the error message.

The name of the TLS configuration to use.

If a name is configured, it uses the configuration from quarkus.tls.<name>.* If a name is configured, but no TLS configuration is found with that name then an error will be thrown.

The default TLS configuration is not used by default.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TLS_CONFIGURATION_NAME

Show more

string

If set to true then binary/text messages received/sent are logged if the DEBUG level is enabled for the logger io.quarkus.websockets.next.traffic.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TRAFFIC_LOGGING_ENABLED

Show more

ブーリアン

false

The number of characters of a text message which will be logged if traffic logging is enabled. The payload of a binary message is never logged.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TRAFFIC_LOGGING_TEXT_PAYLOAD_LIMIT

Show more

int

100

If collection of WebSocket traces is enabled. Only applicable when the OpenTelemetry extension is present.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TRACES_ENABLED

Show more

ブーリアン

true

If collection of WebSocket metrics is enabled. Only applicable when the Micrometer extension is present.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_METRICS_ENABLED

Show more

ブーリアン

false

期間フォーマットについて

期間の値を書くには、標準の java.time.Duration フォーマットを使います。 詳細は Duration#parse() Java API documentation を参照してください。

数字で始まる簡略化した書式を使うこともできます:

  • 数値のみの場合は、秒単位の時間を表します。

  • 数値の後に ms が続く場合は、ミリ秒単位の時間を表します。

その他の場合は、簡略化されたフォーマットが解析のために java.time.Duration フォーマットに変換されます:

  • 数値の後に hms が続く場合は、その前に PT が付けられます。

  • 数値の後に d が続く場合は、その前に P が付けられます。

関連コンテンツ