The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

WebSocketの使用

このガイドでは、QuarkusアプリケーションがWebSocketを利用してインタラクティブなウェブアプリケーションを作成する方法を説明します。 定型的な WebSocketアプリケーションなので、簡単なチャットアプリケーションを作成します。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

アーキテクチャ

このガイドでは、WebSocketを使用して、接続されている他のユーザーとメッセージを送受信するための簡単なチャットアプリケーションを作成します。

Architecture

ソリューション

次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、すぐに完成した例に飛んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

ソリューションは websockets-quickstart ディレクトリ にあります。

Mavenプロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。

コマンドラインインタフェース
quarkus create app org.acme:websockets-quickstart \
    --extension='websockets' \
    --no-code
cd websockets-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.9.3:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=websockets-quickstart \
    -Dextensions='websockets' \
    -DnoCode
cd websockets-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=websockets-quickstart"

このコマンドは、Mavenプロジェクト(クラスなし)を生成し、 websockets エクステンションをインポートします。

すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに undertow-websockets エクステンションを追加することができます。

コマンドラインインタフェース
quarkus extension add websockets
Maven
./mvnw quarkus:add-extension -Dextensions='websockets'
Gradle
./gradlew addExtension --extensions='websockets'

これにより、ビルドファイルに以下が追加されます:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets</artifactId>
</dependency>
build.gradle
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
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

そして、ブラウザウィンドウを2つ開いて、 http://localhost:8080/ に移動します:

  1. 上部のテキストエリアに名前を入力します(2種類の名前を使用します)。

  2. connectをクリック

  3. メッセージの送受信

Application

いつものように、アプリケーションは以下の方法でパッケージ化されます。

コマンドラインインタフェース
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

そして、 java -jar target/quarkus-app/quarkus-run.jar を使って実行します。

ネイティブ実行可能ファイルを次のようにビルドすることもできます。

コマンドラインインタフェース
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

また、ここ で説明されたアプローチを使用した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 の実装の一つです。

関連コンテンツ