Skip to content

Commit

Permalink
Initial version of the new declarative WebSocket server API
Browse files Browse the repository at this point in the history
- see also #38473

Co-authored-by: Clement Escoffier <[email protected]>
  • Loading branch information
mkouba and cescoffier committed Mar 5, 2024
1 parent 78ece28 commit 8c7286e
Show file tree
Hide file tree
Showing 98 changed files with 6,280 additions and 5 deletions.
10 changes: 10 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2075,6 +2075,16 @@
<artifactId>quarkus-websockets-client-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets-next</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets-next-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-undertow-spi</artifactId>
Expand Down
1 change: 1 addition & 0 deletions extensions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<module>vertx-http</module>
<module>undertow</module>
<module>websockets</module>
<module>websockets-next</module>
<module>webjars-locator</module>
<module>resteasy-reactive</module>
<module>reactive-routes</module>
Expand Down
20 changes: 20 additions & 0 deletions extensions/websockets-next/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-extensions-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-websockets-next-aggregator</artifactId>
<name>Quarkus - WebSockets Next Aggregator</name>
<packaging>pom</packaging>
<modules>
<module>server</module>
</modules>

</project>
72 changes: 72 additions & 0 deletions extensions/websockets-next/server/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-websockets-next-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-websockets-next-deployment</artifactId>
<name>Quarkus - WebSockets Next - Deployment</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets-next</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>
org.jboss.logmanager.LogManager</java.util.logging.manager>
<quarkus.log.level>INFO</quarkus.log.level>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.websockets.next.deployment;

import io.quarkus.builder.item.MultiBuildItem;

/**
* A generated {@link io.quarkus.websockets.next.runtime.WebSocketEndpoint}.
*/
final class GeneratedEndpointBuildItem extends MultiBuildItem {

final String className;
final String path;

GeneratedEndpointBuildItem(String className, String path) {
this.className = className;
this.path = path;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.websockets.next.deployment;

import org.jboss.jandex.DotName;

import io.quarkus.websockets.next.BinaryMessage;
import io.quarkus.websockets.next.OnClose;
import io.quarkus.websockets.next.OnMessage;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.TextMessage;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketServerConnection;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.common.annotation.RunOnVirtualThread;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

final class WebSocketDotNames {

static final DotName WEB_SOCKET = DotName.createSimple(WebSocket.class);
static final DotName WEB_SOCKET_CONNECTION = DotName.createSimple(WebSocketServerConnection.class);
static final DotName ON_OPEN = DotName.createSimple(OnOpen.class);
static final DotName ON_MESSAGE = DotName.createSimple(OnMessage.class);
static final DotName ON_CLOSE = DotName.createSimple(OnClose.class);
static final DotName UNI = DotName.createSimple(Uni.class);
static final DotName MULTI = DotName.createSimple(Multi.class);
static final DotName RUN_ON_VIRTUAL_THREAD = DotName.createSimple(RunOnVirtualThread.class);
static final DotName BLOCKING = DotName.createSimple(Blocking.class);
static final DotName STRING = DotName.createSimple(String.class);
static final DotName BUFFER = DotName.createSimple(Buffer.class);
static final DotName JSON_OBJECT = DotName.createSimple(JsonObject.class);
static final DotName JSON_ARRAY = DotName.createSimple(JsonArray.class);
static final DotName VOID = DotName.createSimple(Void.class);
static final DotName BINARY_MESSAGE = DotName.createSimple(BinaryMessage.class);
static final DotName TEXT_MESSAGE = DotName.createSimple(TextMessage.class);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package io.quarkus.websockets.next.deployment;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.PrimitiveType;
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;

import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.runtime.WebSocketEndpoint;
import io.quarkus.websockets.next.runtime.WebSocketEndpoint.ExecutionModel;
import io.quarkus.websockets.next.runtime.WebSocketEndpoint.MessageType;

/**
* This build item represents a WebSocket endpoint class.
*/
public final class WebSocketEndpointBuildItem extends MultiBuildItem {

public final BeanInfo bean;
public final String path;
public final WebSocket.ExecutionMode executionMode;
public final Callback onOpen;
public final Callback onMessage;
public final Callback onClose;

public WebSocketEndpointBuildItem(BeanInfo bean, String path, WebSocket.ExecutionMode executionMode, Callback onOpen,
Callback onMessage, Callback onClose) {
this.bean = bean;
this.path = path;
this.executionMode = executionMode;
this.onOpen = onOpen;
this.onMessage = onMessage;
this.onClose = onClose;
}

public static class Callback {

public final AnnotationInstance annotation;
public final MethodInfo method;
public final ExecutionModel executionModel;
public final MessageType consumedMessageType;
public final MessageType producedMessageType;

public Callback(AnnotationInstance annotation, MethodInfo method, ExecutionModel executionModel) {
this.method = method;
this.annotation = annotation;
this.executionModel = executionModel;
this.consumedMessageType = initMessageType(method.parameters().isEmpty() ? null : method.parameterType(0));
this.producedMessageType = initMessageType(method.returnType());
}

public Type returnType() {
return method.returnType();
}

public Type messageParamType() {
return acceptsMessage() ? method.parameterType(0) : null;
}

public boolean isReturnTypeVoid() {
return returnType().kind() == Kind.VOID;
}

public boolean isReturnTypeUni() {
return WebSocketDotNames.UNI.equals(returnType().name());
}

public boolean isReturnTypeMulti() {
return WebSocketDotNames.MULTI.equals(returnType().name());
}

public boolean acceptsMessage() {
return consumedMessageType != MessageType.NONE;
}

public boolean acceptsBinaryMessage() {
return consumedMessageType == MessageType.BINARY;
}

public boolean acceptsMulti() {
return acceptsMessage() && method.parameterType(0).name().equals(WebSocketDotNames.MULTI);
}

public WebSocketEndpoint.MessageType consumedMessageType() {
return consumedMessageType;
}

public WebSocketEndpoint.MessageType producedMessageType() {
return producedMessageType;
}

public boolean broadcast() {
AnnotationValue broadcastValue = annotation.value("broadcast");
return broadcastValue != null && broadcastValue.asBoolean();
}

public DotName getInputCodec() {
return getCodec("inputCodec");
}

public DotName getOutpuCodec() {
DotName output = getCodec("outputCodec");
return output != null ? output : getInputCodec();
}

private DotName getCodec(String valueName) {
AnnotationInstance messageAnnotation = method.declaredAnnotation(WebSocketDotNames.BINARY_MESSAGE);
if (messageAnnotation == null) {
messageAnnotation = method.declaredAnnotation(WebSocketDotNames.TEXT_MESSAGE);
}
if (messageAnnotation != null) {
AnnotationValue codecValue = messageAnnotation.value(valueName);
if (codecValue != null) {
return codecValue.asClass().name();
}
}
return null;
}

MessageType initMessageType(Type messageType) {
MessageType ret = MessageType.NONE;
if (messageType != null && !messageType.name().equals(WebSocketDotNames.VOID)) {
if (method.hasDeclaredAnnotation(WebSocketDotNames.BINARY_MESSAGE)) {
ret = MessageType.BINARY;
} else if (method.hasDeclaredAnnotation(WebSocketDotNames.TEXT_MESSAGE)) {
ret = MessageType.TEXT;
} else {
if (isByteArray(messageType) || WebSocketDotNames.BUFFER.equals(messageType.name())) {
ret = MessageType.BINARY;
} else {
ret = MessageType.TEXT;
}
}
}
return ret;
}

static boolean isByteArray(Type type) {
return type.kind() == Kind.ARRAY && PrimitiveType.BYTE.equals(type.asArrayType().constituent());
}

static boolean isUniVoid(Type type) {
return WebSocketDotNames.UNI.equals(type.name())
&& type.asParameterizedType().arguments().get(0).name().equals(WebSocketDotNames.VOID);
}

}

}
Loading

0 comments on commit 8c7286e

Please sign in to comment.