Usando WebSockets
Este guia explica como seu aplicativo Quarkus pode utilizar web sockets para criar aplicativos web interativas. Por ser a aplicação canônica de web socket, vamos criar um aplicativo de bate-papo simples.
Pré-requisitos
Para concluir este guia, você precisa:
-
Cerca de 15 minutos
-
Um IDE
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
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.
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:
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:
quarkus extension add websockets
./mvnw quarkus:add-extension -Dextensions='websockets'
./gradlew addExtension --extensions='websockets'
Isto irá adicionar o seguinte trecho no seu arquivo de build:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</dependency>
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:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
Em seguida, abra as duas janelas do navegador em http://localhost:8080/:
-
Digite um nome na área de texto superior (use dois nomes diferentes).
-
Clique em conectar
-
Enviar e receber mensagens
Como de costume, a aplicação pode ser empacotada utilizando:
quarkus build
./mvnw install
./gradlew build
E executada com java -jar target/quarkus-app/quarkus-run.jar
.
Você também pode criar o executável nativo usando:
quarkus build --native
./mvnw install -Dnative
./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 .