Skip to content

Commit

Permalink
First attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
mkouba committed Apr 25, 2024
1 parent 7470de8 commit 43da19c
Show file tree
Hide file tree
Showing 61 changed files with 950 additions and 337 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.WebSocketConnection;
import io.quarkus.websockets.next.WebSocketServerException;
import io.quarkus.websockets.next.WebSocketException;

/**
* Provides arguments for method parameters of a callback method declared on a WebSocket endpoint.
Expand All @@ -24,7 +24,7 @@ interface CallbackArgument {
*
* @param context
* @return {@code true} if this provider matches the given parameter context, {@code false} otherwise
* @throws WebSocketServerException If an invalid parameter is detected
* @throws WebSocketException If an invalid parameter is detected
*/
boolean matches(ParameterContext context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.websockets.next.HandshakeRequest;
import io.quarkus.websockets.next.WebSocketConnection;

class HandshakeRequestCallbackArgument implements CallbackArgument {
Expand All @@ -15,7 +16,7 @@ public boolean matches(ParameterContext context) {
public ResultHandle get(InvocationBytecodeContext context) {
ResultHandle connection = context.getConnection();
return context.bytecode().invokeInterfaceMethod(MethodDescriptor.ofMethod(WebSocketConnection.class, "handshakeRequest",
WebSocketConnection.HandshakeRequest.class), connection);
HandshakeRequest.class), connection);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.websockets.next.WebSocketConnection;
import io.quarkus.websockets.next.WebSocketServerException;
import io.quarkus.websockets.next.WebSocketException;

class PathParamCallbackArgument implements CallbackArgument {

Expand All @@ -20,16 +20,16 @@ public boolean matches(ParameterContext context) {
String name = getParamName(context);
if (name != null) {
if (!context.parameter().type().name().equals(WebSocketDotNames.STRING)) {
throw new WebSocketServerException("Method parameter annotated with @PathParam must be java.lang.String: "
throw new WebSocketException("Method parameter annotated with @PathParam must be java.lang.String: "
+ WebSocketServerProcessor.callbackToString(context.parameter().method()));
}
if (context.endpointPath() == null) {
throw new WebSocketServerException("Global error handlers may not accept @PathParam parameters: "
throw new WebSocketException("Global error handlers may not accept @PathParam parameters: "
+ WebSocketServerProcessor.callbackToString(context.parameter().method()));
}
List<String> pathParams = getPathParamNames(context.endpointPath());
if (!pathParams.contains(name)) {
throw new WebSocketServerException(
throw new WebSocketException(
String.format(
"@PathParam name [%s] must be used in the endpoint path [%s]: %s", name,
context.endpointPath(),
Expand Down Expand Up @@ -61,7 +61,7 @@ private String getParamName(ParameterContext context) {
name = context.parameter().name();
}
if (name == null) {
throw new WebSocketServerException(String.format(
throw new WebSocketException(String.format(
"Unable to extract the path parameter name - method parameter names not recorded for %s: compile the class with -parameters",
context.parameter().method().declaringClass().name()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.jboss.jandex.DotName;

import io.quarkus.websockets.next.HandshakeRequest;
import io.quarkus.websockets.next.OnBinaryMessage;
import io.quarkus.websockets.next.OnClose;
import io.quarkus.websockets.next.OnError;
Expand Down Expand Up @@ -43,7 +44,7 @@ final class WebSocketDotNames {
static final DotName JSON_ARRAY = DotName.createSimple(JsonArray.class);
static final DotName VOID = DotName.createSimple(Void.class);
static final DotName PATH_PARAM = DotName.createSimple(PathParam.class);
static final DotName HANDSHAKE_REQUEST = DotName.createSimple(WebSocketConnection.HandshakeRequest.class);
static final DotName HANDSHAKE_REQUEST = DotName.createSimple(HandshakeRequest.class);
static final DotName THROWABLE = DotName.createSimple(Throwable.class);

static final List<DotName> CALLBACK_ANNOTATIONS = List.of(ON_OPEN, ON_CLOSE, ON_BINARY_MESSAGE, ON_TEXT_MESSAGE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import io.quarkus.gizmo.BytecodeCreator;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.MessageProcessingMode;
import io.quarkus.websockets.next.WebSocketConnection;
import io.quarkus.websockets.next.WebSocketServerException;
import io.quarkus.websockets.next.WebSocketException;
import io.quarkus.websockets.next.deployment.CallbackArgument.InvocationBytecodeContext;
import io.quarkus.websockets.next.deployment.CallbackArgument.ParameterContext;
import io.quarkus.websockets.next.runtime.WebSocketEndpoint.ExecutionModel;
Expand All @@ -38,15 +38,15 @@ public final class WebSocketEndpointBuildItem extends MultiBuildItem {
public final BeanInfo bean;
public final String path;
public final String endpointId;
public final WebSocket.ExecutionMode executionMode;
public final MessageProcessingMode executionMode;
public final Callback onOpen;
public final Callback onTextMessage;
public final Callback onBinaryMessage;
public final Callback onPongMessage;
public final Callback onClose;
public final List<Callback> onErrors;

WebSocketEndpointBuildItem(BeanInfo bean, String path, String endpointId, WebSocket.ExecutionMode executionMode,
WebSocketEndpointBuildItem(BeanInfo bean, String path, String endpointId, MessageProcessingMode executionMode,
Callback onOpen,
Callback onTextMessage, Callback onBinaryMessage, Callback onPongMessage, Callback onClose,
List<Callback> onErrors) {
Expand Down Expand Up @@ -203,15 +203,15 @@ private List<CallbackArgument> collectArguments(AnnotationInstance annotation, M
DotNames.simpleName(annotation.name()),
parameter.name() != null ? parameter.name() : "#" + parameter.position(),
WebSocketServerProcessor.callbackToString(method));
throw new WebSocketServerException(msg);
throw new WebSocketException(msg);
} else if (found.size() > 1 && (found.get(0).priotity() == found.get(1).priotity())) {
String msg = String.format(
"Unable to inject @%s callback parameter '%s' declared on %s: ambiguous injectors found: %s",
DotNames.simpleName(annotation.name()),
parameter.name() != null ? parameter.name() : "#" + parameter.position(),
WebSocketServerProcessor.callbackToString(method),
found.stream().map(p -> p.getClass().getSimpleName() + ":" + p.priotity()));
throw new WebSocketServerException(msg);
throw new WebSocketException(msg);
}
arguments.add(found.get(0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;
import io.quarkus.websockets.next.MessageProcessingMode;
import io.quarkus.websockets.next.TextMessageCodec;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketConnection;
import io.quarkus.websockets.next.WebSocketServerException;
import io.quarkus.websockets.next.WebSocketException;
import io.quarkus.websockets.next.WebSocketsServerRuntimeConfig;
import io.quarkus.websockets.next.deployment.WebSocketEndpointBuildItem.Callback;
import io.quarkus.websockets.next.deployment.WebSocketEndpointBuildItem.Callback.MessageType;
Expand Down Expand Up @@ -151,7 +151,7 @@ public void collectEndpoints(BeanArchiveIndexBuildItem beanArchiveIndex,
GlobalErrorHandler errorHandler = new GlobalErrorHandler(bean, callback);
DotName errorTypeName = callback.argumentType(ErrorCallbackArgument::isError).name();
if (globalErrors.containsKey(errorTypeName)) {
throw new WebSocketServerException(String.format(
throw new WebSocketException(String.format(
"Multiple global @OnError callbacks may not accept the same error parameter: %s\n\t- %s\n\t- %s",
errorTypeName,
callbackToString(callback.method),
Expand All @@ -177,7 +177,7 @@ public void collectEndpoints(BeanArchiveIndexBuildItem beanArchiveIndex,
}
DotName prevPath = pathToEndpoint.put(path, beanClass.name());
if (prevPath != null) {
throw new WebSocketServerException(
throw new WebSocketException(
String.format("Multiple endpoints [%s, %s] define the same path: %s", prevPath, beanClass, path));
}
String endpointId;
Expand All @@ -189,7 +189,7 @@ public void collectEndpoints(BeanArchiveIndexBuildItem beanArchiveIndex,
}
DotName prevId = idToEndpoint.put(endpointId, beanClass.name());
if (prevId != null) {
throw new WebSocketServerException(
throw new WebSocketException(
String.format("Multiple endpoints [%s, %s] define the same endpoint id: %s", prevId, beanClass,
endpointId));
}
Expand All @@ -206,14 +206,14 @@ public void collectEndpoints(BeanArchiveIndexBuildItem beanArchiveIndex,
callbackArguments, transformedAnnotations, path,
this::validateOnClose);
if (onOpen == null && onTextMessage == null && onBinaryMessage == null && onPongMessage == null) {
throw new WebSocketServerException(
throw new WebSocketException(
"The endpoint must declare at least one method annotated with @OnTextMessage, @OnBinaryMessage, @OnPongMessage or @OnOpen: "
+ beanClass);
}
AnnotationValue executionMode = webSocketAnnotation.value("executionMode");
endpoints.produce(new WebSocketEndpointBuildItem(bean, path, endpointId,
executionMode != null ? WebSocket.ExecutionMode.valueOf(executionMode.asEnum())
: WebSocket.ExecutionMode.SERIAL,
executionMode != null ? MessageProcessingMode.valueOf(executionMode.asEnum())
: MessageProcessingMode.SERIAL,
onOpen,
onTextMessage,
onBinaryMessage,
Expand Down Expand Up @@ -346,7 +346,7 @@ static String getPath(String path) {
if (end < path.length()) {
char nextChar = path.charAt(end);
if (Character.isAlphabetic(nextChar) || Character.isDigit(nextChar) || nextChar == '_') {
throw new WebSocketServerException("Path parameter " + match
throw new WebSocketException("Path parameter " + match
+ " may not be followed by an alphanumeric character or underscore: " + path);
}
}
Expand All @@ -363,7 +363,7 @@ static String callbackToString(MethodInfo callback) {
private String getPathPrefix(IndexView index, DotName enclosingClassName) {
ClassInfo enclosingClass = index.getClassByName(enclosingClassName);
if (enclosingClass == null) {
throw new WebSocketServerException("Enclosing class not found in index: " + enclosingClass);
throw new WebSocketException("Enclosing class not found in index: " + enclosingClass);
}
AnnotationInstance webSocketAnnotation = enclosingClass.annotation(WebSocketDotNames.WEB_SOCKET);
if (webSocketAnnotation != null) {
Expand All @@ -379,20 +379,20 @@ private String getPathPrefix(IndexView index, DotName enclosingClassName) {

private void validateOnPongMessage(Callback callback) {
if (callback.returnType().kind() != Kind.VOID && !WebSocketServerProcessor.isUniVoid(callback.returnType())) {
throw new WebSocketServerException(
throw new WebSocketException(
"@OnPongMessage callback must return void or Uni<Void>: " + callbackToString(callback.method));
}
Type messageType = callback.argumentType(MessageCallbackArgument::isMessage);
if (messageType == null || !messageType.name().equals(WebSocketDotNames.BUFFER)) {
throw new WebSocketServerException(
throw new WebSocketException(
"@OnPongMessage callback must accept exactly one message parameter of type io.vertx.core.buffer.Buffer: "
+ callbackToString(callback.method));
}
}

private void validateOnClose(Callback callback) {
if (callback.returnType().kind() != Kind.VOID && !WebSocketServerProcessor.isUniVoid(callback.returnType())) {
throw new WebSocketServerException(
throw new WebSocketException(
"@OnClose callback must return void or Uni<Void>: " + callbackToString(callback.method));
}
}
Expand Down Expand Up @@ -486,7 +486,7 @@ private String generateEndpoint(WebSocketEndpointBuildItem endpoint,
constructor.getMethodParam(2), constructor.getMethodParam(3));
constructor.returnNull();

MethodCreator executionMode = endpointCreator.getMethodCreator("executionMode", WebSocket.ExecutionMode.class);
MethodCreator executionMode = endpointCreator.getMethodCreator("executionMode", MessageProcessingMode.class);
executionMode.returnValue(executionMode.load(endpoint.executionMode));

MethodCreator beanIdentifier = endpointCreator.getMethodCreator("beanIdentifier", String.class);
Expand Down Expand Up @@ -548,7 +548,7 @@ private void generateOnError(ClassCreator endpointCreator, WebSocketEndpointBuil
for (Callback callback : endpoint.onErrors) {
DotName errorTypeName = callback.argumentType(ErrorCallbackArgument::isError).name();
if (errors.containsKey(errorTypeName)) {
throw new WebSocketServerException(String.format(
throw new WebSocketException(String.format(
"Multiple @OnError callbacks may not accept the same error parameter: %s\n\t- %s\n\t- %s",
errorTypeName,
callbackToString(callback.method), callbackToString(errors.get(errorTypeName).method)));
Expand Down Expand Up @@ -1030,7 +1030,7 @@ private List<Callback> findErrorHandlers(IndexView index, ClassInfo beanClass, C
transformedAnnotations, endpointPath, index);
long errorArguments = callback.arguments.stream().filter(ca -> ca instanceof ErrorCallbackArgument).count();
if (errorArguments != 1) {
throw new WebSocketServerException(
throw new WebSocketException(
String.format("@OnError callback must accept exactly one error parameter; found %s: %s",
errorArguments,
callbackToString(callback.method)));
Expand Down Expand Up @@ -1078,15 +1078,15 @@ private Callback findCallback(IndexView index, ClassInfo beanClass, DotName anno
long messageArguments = callback.arguments.stream().filter(ca -> ca instanceof MessageCallbackArgument).count();
if (callback.acceptsMessage()) {
if (messageArguments > 1) {
throw new WebSocketServerException(
throw new WebSocketException(
String.format("@%s callback may accept at most 1 message parameter; found %s: %s",
DotNames.simpleName(callback.annotation.name()),
messageArguments,
callbackToString(callback.method)));
}
} else {
if (messageArguments != 0) {
throw new WebSocketServerException(
throw new WebSocketException(
String.format("@%s callback must not accept a message parameter; found %s: %s",
DotNames.simpleName(callback.annotation.name()),
messageArguments,
Expand All @@ -1098,7 +1098,7 @@ private Callback findCallback(IndexView index, ClassInfo beanClass, DotName anno
}
return callback;
}
throw new WebSocketServerException(
throw new WebSocketException(
String.format("There can be only one callback annotated with %s declared on %s", annotationName, beanClass));
}

Expand All @@ -1124,7 +1124,7 @@ boolean hasBlockingSignature(MethodInfo method) {
DotName name = method.returnType().asParameterizedType().name();
return !name.equals(WebSocketDotNames.UNI) && !name.equals(WebSocketDotNames.MULTI);
default:
throw new WebSocketServerException("Unsupported return type:" + callbackToString(method));
throw new WebSocketException("Unsupported return type:" + callbackToString(method));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import org.junit.jupiter.api.Test;

import io.quarkus.websockets.next.WebSocketServerException;
import io.quarkus.websockets.next.WebSocketException;

public class WebSocketServerProcessorTest {

Expand All @@ -15,17 +15,17 @@ public void testGetPath() {
assertEquals("/foo/:id/bar/:id2", WebSocketServerProcessor.getPath("/foo/{id}/bar/{id2}"));
assertEquals("/foo/:bar-:baz", WebSocketServerProcessor.getPath("/foo/{bar}-{baz}"));
assertEquals("/ws/v:version", WebSocketServerProcessor.getPath("/ws/v{version}"));
WebSocketServerException e = assertThrows(WebSocketServerException.class,
WebSocketException e = assertThrows(WebSocketException.class,
() -> WebSocketServerProcessor.getPath("/foo/v{bar}/{baz}and{alpha_1}-{name}"));
assertEquals(
"Path parameter {baz} may not be followed by an alphanumeric character or underscore: /foo/v{bar}/{baz}and{alpha_1}-{name}",
e.getMessage());
e = assertThrows(WebSocketServerException.class,
e = assertThrows(WebSocketException.class,
() -> WebSocketServerProcessor.getPath("/foo/v{bar}/{baz}_{alpha_1}-{name}"));
assertEquals(
"Path parameter {baz} may not be followed by an alphanumeric character or underscore: /foo/v{bar}/{baz}_{alpha_1}-{name}",
e.getMessage());
e = assertThrows(WebSocketServerException.class,
e = assertThrows(WebSocketException.class,
() -> WebSocketServerProcessor.getPath("/foo/v{bar}/{baz}1-{name}"));
assertEquals(
"Path parameter {baz} may not be followed by an alphanumeric character or underscore: /foo/v{bar}/{baz}1-{name}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.websockets.next.HandshakeRequest;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketConnection.HandshakeRequest;
import io.quarkus.websockets.next.test.utils.WSClient;
import io.vertx.core.Vertx;
import io.vertx.core.http.WebSocketConnectOptions;
Expand Down
Loading

0 comments on commit 43da19c

Please sign in to comment.