WebSocketの使用
このガイドでは、QuarkusアプリケーションがWebSocketを利用してインタラクティブなウェブアプリケーションを作成する方法を説明します。 定型的な WebSocketアプリケーションを想定し、簡単なチャットアプリケーションを作成します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 11+ がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.8.1+
-
使用したい場合、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、すぐに完成した例に飛んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
ソリューションは websockets-quickstart
ディレクトリ にあります。
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
このコマンドは、Mavenプロジェクト(クラスなし)を生成し、 websockets
エクステンションをインポートします。
すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに 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の取り扱い
このアプリケーションには、Web ソケットを処理するクラスが一つ含まれます。 src/main/java
ディレクトリーに org.acme.websockets.ChatSocket
クラスを作成します。作成したファイルに以下の内容をコピーします。
package org.acme.websockets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.enterprise.context.ApplicationScoped;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import javax.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) {
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) {
if (message.equalsIgnoreCase("_ready_")) {
broadcast("User " + username + " joined");
} else {
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 clean package
./gradlew build
そして、 java -jar target/quarkus-app/quarkus-run.jar
を使って実行します。
ネイティブ実行可能ファイルを次のようにビルドすることもできます。
quarkus build --native
./mvnw package -Dnative
./gradlew build -Dquarkus.package.type=native
また、https://github.com/quarkusio/quarkus-quickstarts/blob/main/websockets-quickstart/src/test/java/org/acme/websockets/ChatTest.java[こちら] で詳細に解説された手法を使用して、Webソケットアプリケーションをテストすることもできます。
WebSocketクライアント
Quarkusには、WebSocketクライアントも含まれています。 ContainerProvider.getWebSocketContainer().connectToServer
を呼び出して、WebSocket 接続を作成できます。デフォルトでは、 quarkus-websockets
アーティファクトにはクライアントとサーバーの両方が含まれていますが、クライアントのみを必要とする場合は、代わりに quarkus-websockets-client
を含めることができます。
サーバーに接続する際には、使用するアノテーション付きクライアント・エンドポイントの Class で渡すか、 javax.websocket.Endpoint
のインスタンスで渡すことができます。アノテーション付きエンドポイントを使用している場合は、サーバー上で使用できるのとまったく同じアノテーションを使用できますが、アノテーションは @ServerEndpoint
ではなく @ClientEndpoint
でなければなりません。
以下の例は、上記のチャットエンドポイントをテストするために使用されるクライアントを示しています。
package org.acme.websockets;
import java.net.URI;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.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 の実装の一つです。