WebSocketの使用
このガイドでは、QuarkusアプリケーションがWebSocketを利用してインタラクティブなウェブアプリケーションを作成する方法を説明します。 定型的な WebSocketアプリケーションなので、簡単なチャットアプリケーションを作成します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、すぐに完成した例に飛んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
ソリューションは websockets-quickstart
ディレクトリ にあります。
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=websockets-quickstart"
このコマンドは、Mavenプロジェクト(クラスなし)を生成し、 websockets
エクステンションをインポートします。
すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに undertow-websockets
エクステンションを追加することができます。
quarkus extension add websockets
./mvnw quarkus:add-extension -Dextensions='websockets'
./gradlew addExtension --extensions='websockets'
これにより、ビルドファイルに以下が追加されます:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</dependency>
implementation("io.quarkus:quarkus-websockets")
WebSocketクライアントのみを使用したい場合は、代わりに quarkus-websockets-client を含める必要があります。
|
WebSocketの取り扱い
このアプリケーションには、websocketを処理するクラスが一つ含まれます。 src/main/java
ディレクトリーに org.acme.websockets.ChatSocket
クラスを作成します。作成したファイルに以下の内容をコピーします。
package org.acme.websockets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import jakarta.websocket.Session;
@ServerEndpoint("/chat/{username}") (1)
@ApplicationScoped
public class ChatSocket {
Map<String, Session> sessions = new ConcurrentHashMap<>(); (2)
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
broadcast("User " + username + " joined");
sessions.put(username, session);
}
@OnClose
public void onClose(Session session, @PathParam("username") String username) {
sessions.remove(username);
broadcast("User " + username + " left");
}
@OnError
public void onError(Session session, @PathParam("username") String username, Throwable throwable) {
sessions.remove(username);
broadcast("User " + username + " left on error: " + throwable);
}
@OnMessage
public void onMessage(String message, @PathParam("username") String username) {
broadcast(">> " + username + ": " + message);
}
private void broadcast(String message) {
sessions.values().forEach(s -> {
s.getAsyncRemote().sendObject(message, result -> {
if (result.getException() != null) {
System.out.println("Unable to send message: " + result.getException());
}
});
});
}
}
1 | WebSocketのURLを設定します |
2 | 現在開いているWebSocketを格納します |
洗練されたWebフロントエンド
すべてのチャットアプリケーションには 素敵な UIが必要です。
Quarkusは、 META-INF/resources
ディレクトリに含まれる静的リソースを自動的に提供します。
src/main/resources/META-INF/resources
ディレクトリを作成し、この index.html ファイルをコピーしてください。
アプリケーションの実行
では、実際にアプリケーションを見てみましょう。以下のように実行してみてください:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
そして、ブラウザウィンドウを2つ開いて、 http://localhost:8080/ に移動します:
-
上部のテキストエリアに名前を入力します(2種類の名前を使用します)。
-
connectをクリック
-
メッセージの送受信
いつものように、アプリケーションは以下の方法でパッケージ化されます。
quarkus build
./mvnw install
./gradlew build
そして、 java -jar target/quarkus-app/quarkus-run.jar
を使って実行します。
ネイティブ実行可能ファイルを次のようにビルドすることもできます。
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
また、ここ で説明されたアプローチを使用したWebソケットアプリケーションをテストすることもできます。
WebSocketクライアント
Quarkusには、WebSocketクライアントも含まれています。 ContainerProvider.getWebSocketContainer().connectToServer
を呼び出して、WebSocket 接続を作成できます。デフォルトでは、 quarkus-websockets
アーティファクトにはクライアントとサーバーの両方が含まれていますが、クライアントのみを必要とする場合は、代わりに quarkus-websockets-client
を含めることができます。
サーバーに接続する際、使用するアノテーション付きクライアントエンドポイントのクラス、または jakarta.websocket.Endpoint
のインスタンスを渡すことができます。アノテーション付きエンドポイントを使用する場合、 @ServerEndpoint
の代わりに @ClientEndpoint
でアノテーションする必要がありますが、サーバー上で使用できるものと全く同じアノテーションを使用することができます。
以下の例は、上記のチャットエンドポイントをテストするために使用されるクライアントを示しています。
package org.acme.websockets;
import java.net.URI;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
public class ChatTest {
private static final LinkedBlockingDeque<String> MESSAGES = new LinkedBlockingDeque<>();
@TestHTTPResource("/chat/stu")
URI uri;
@Test
public void testWebsocketChat() throws Exception {
try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(Client.class, uri)) {
Assertions.assertEquals("CONNECT", MESSAGES.poll(10, TimeUnit.SECONDS));
Assertions.assertEquals("User stu joined", MESSAGES.poll(10, TimeUnit.SECONDS));
session.getAsyncRemote().sendText("hello world");
Assertions.assertEquals(">> stu: hello world", MESSAGES.poll(10, TimeUnit.SECONDS));
}
}
@ClientEndpoint
public static class Client {
@OnOpen
public void open(Session session) {
MESSAGES.add("CONNECT");
// Send a message to indicate that we are ready,
// as the message handler may not be registered immediately after this callback.
session.getAsyncRemote().sendText("_ready_");
}
@OnMessage
void message(String msg) {
MESSAGES.add(msg);
}
}
}
その他のWebSocket情報
Quarkus WebSocketの実装は、 Jakarta Websocket の実装の一つです。