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

Using WebSockets with Undertow

This guide explains how your Quarkus application can utilize web sockets to create interactive web applications, in the context of an Undertow-based Quarkus application, or if you rely on Jakarta WebSocket.

If you don’t use Undertow or Jakarta WebSocket, it is recommended to use the more modern WebSockets Next extensions.

Because it’s the canonical web socket application, we are going to create a simple chat application.

Pré-requisitos

Para concluir este guia, você precisa:

  • Cerca de 15 minutos

  • Um IDE

  • JDK 17+ instalado com JAVA_HOME configurado corretamente

  • Apache Maven 3.9.9

  • Opcionalmente, o Quarkus CLI se você quiser usá-lo

  • Opcionalmente, Mandrel ou GraalVM instalado e configurado apropriadamente se você quiser criar um executável nativo (ou Docker se você usar uma compilação de contêiner nativo)

Arquitetura

Neste guia, criamos um aplicativo de bate-papo simples usando soquetes da Web para receber e enviar mensagens a outros usuários conectados.

Architecture

Solução

Recomendamos que você siga as instruções nas próximas seções e crie o aplicativo passo a passo. No entanto, você pode pular diretamente para o exemplo completo.

Clone o repositório Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git, ou baixe um arquivo.

A solução está localizada no websockets-quickstart diretório.

Criar o projeto Maven

Primeiro, precisamos de um novo projeto. Crie um novo projeto com o seguinte comando:

CLI
quarkus create app org.acme:websockets-quickstart \
    --extension='websockets' \
    --no-code
cd websockets-quickstart

Para criar um projeto Gradle, adicione a opção --gradle ou --gradle-kotlin-dsl.

Para obter mais informações sobre como instalar e usar a CLI do Quarkus, consulte o guia Quarkus CLI.

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

Para criar um projeto Gradle, adicione a opção '-DbuildTool=gradle' ou '-DbuildTool=gradle-kotlin-dsl'.

Para usuários do Windows:

  • Se estiver usando cmd, (não use barra invertida '\' e coloque tudo na mesma linha)

  • Se estiver usando o Powershell, envolva os parâmetros '-D' entre aspas duplas, por exemplo, '"-DprojectArtifactId=websockets-quickstart"'

Esse comando gera o projeto (sem nenhuma classe) e importa a extensão websockets .

Se você já configurou seu projeto Quarkus, pode adicionar a extensão websockets ao seu projeto executando o seguinte comando no diretório base do seu projeto:

CLI
quarkus extension add websockets
Maven
./mvnw quarkus:add-extension -Dextensions='websockets'
Gradle
./gradlew addExtension --extensions='websockets'

Isto irá adicionar o seguinte trecho no seu arquivo de build:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-websockets")
Se você deseja utilizar apenas o cliente WebSocket, você deve incluir quarkus-websockets-client.

Manuseio de web sockets

Nosso aplicativo contém uma única classe que lida com os Web Sockets. Crie a classe org.acme.websockets.ChatSocket no diretório src/main/java . Copie o seguinte conteúdo para o arquivo criado:

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 Configure a URL do Web Socket
2 Armazena os web sockets atualmente abertos

Um frontend da Web sofisticado

Todos os aplicativos de bate-papo precisam de uma boa interface de usuário. Bem, este pode não ser tão bom, mas funciona. O Quarkus automaticamente serve os recursos estáticos contidos no diretório META-INF/resources . Crie o diretório src/main/resources/META-INF/resources e copie esse arquivo index.html para ele.

Executar o aplicativo

Agora, vamos ver nosso aplicativo em ação. Execute-o com:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

Em seguida, abra as duas janelas do navegador em http://localhost:8080/:

  1. Digite um nome na área de texto superior (use dois nomes diferentes).

  2. Clique em conectar

  3. Enviar e receber mensagens

Application

Como de costume, a aplicação pode ser empacotada utilizando:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

E executada com java -jar target/quarkus-app/quarkus-run.jar.

Você também pode criar o executável nativo usando:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

Você também pode testar seus aplicativos de soquete da Web usando a abordagem detalhada aqui .

Clientes WebSocket

O Quarkus também contém um cliente WebSocket. Você pode chamar ContainerProvider.getWebSocketContainer().connectToServer para criar uma conexão WebSocket. Por padrão, o artefato quarkus-websockets inclui suporte tanto para cliente quanto para servidor. No entanto, se você deseja apenas o cliente, pode incluir quarkus-websockets-client.

Ao se conectar ao servidor, o usuário pode passar a classe do endpoint do cliente anotado que deseja usar ou uma instância de jakarta.websocket.Endpoint . Se estiver usando o endpoint anotado, o usuário poderá usar exatamente as mesmas anotações que pode usar no servidor, exceto que ele deve ser anotado com @ClientEndpoint em vez de @ServerEndpoint .

O exemplo abaixo mostra o client sendo usado para testar o endpoint de bate-papo acima.

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);
        }

    }

}

Mais informações sobre o WebSocket

A implementação do Quarkus WebSocket é uma implementação do Jakarta Websockets .

Conteúdo Relacionado