From 4b19c19815b64b5215777fcbb8b3bdfb72cc473d Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 19 May 2020 22:45:37 +1000 Subject: [PATCH] Issue #3428 - Initial refactor to support javax websocket decoderLists Signed-off-by: Lachlan Roberts --- ...vaxWebSocketClientFrameHandlerFactory.java | 4 - .../common/JavaxWebSocketCallingArgs.java | 79 +++++ .../common/JavaxWebSocketFrameHandler.java | 98 +++--- .../JavaxWebSocketFrameHandlerFactory.java | 280 +++++++----------- .../JavaxWebSocketFrameHandlerMetadata.java | 52 +--- .../common/JavaxWebSocketMessageMetadata.java | 115 +++++++ .../javax/common/JavaxWebSocketSession.java | 19 +- .../common/decoders/AvailableDecoders.java | 185 +++--------- .../common/decoders/RegisteredDecoder.java | 97 ++++++ .../messages/AbstractDecodedMessageSink.java | 75 +++++ .../messages/DecodedBinaryMessageSink.java | 57 ++-- .../DecodedBinaryStreamMessageSink.java | 48 ++- .../common/messages/DecodedMessageSink.java | 64 ---- .../messages/DecodedTextMessageSink.java | 62 ++-- .../DecodedTextStreamMessageSink.java | 49 ++- .../messages/AbstractMessageSinkTest.java | 21 ++ .../DecodedBinaryMessageSinkTest.java | 10 +- .../DecodedBinaryStreamMessageSinkTest.java | 10 +- .../messages/DecodedTextMessageSinkTest.java | 11 +- .../DecodedTextStreamMessageSinkTest.java | 11 +- ...vaxWebSocketServerFrameHandlerFactory.java | 4 - .../tests/matchers/IsMessageHandlerType.java | 4 +- .../IsMessageHandlerTypeRegistered.java | 6 +- .../javax/tests/DecoderListTest.java | 162 ++++++++++ .../tests/coders/AvailableDecodersTest.java | 75 +++-- .../tests/coders/DecoderTextStreamTest.java | 23 +- 26 files changed, 954 insertions(+), 667 deletions(-) create mode 100644 jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketCallingArgs.java create mode 100644 jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketMessageMetadata.java create mode 100644 jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/RegisteredDecoder.java create mode 100644 jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractDecodedMessageSink.java delete mode 100644 jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedMessageSink.java create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/DecoderListTest.java diff --git a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java index 5a1b5252e0ea..06b2b1544322 100644 --- a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java +++ b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java @@ -49,14 +49,10 @@ public EndpointConfig newDefaultEndpointConfig(Class endpointClass) public JavaxWebSocketFrameHandlerMetadata getMetadata(Class endpointClass, EndpointConfig endpointConfig) { if (javax.websocket.Endpoint.class.isAssignableFrom(endpointClass)) - { return createEndpointMetadata((Class)endpointClass, endpointConfig); - } if (endpointClass.getAnnotation(ClientEndpoint.class) == null) - { return null; - } JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig); return discoverJavaxFrameHandlerMetadata(endpointClass, metadata); diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketCallingArgs.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketCallingArgs.java new file mode 100644 index 000000000000..3e8821178c33 --- /dev/null +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketCallingArgs.java @@ -0,0 +1,79 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.javax.common; + +import java.io.InputStream; +import java.io.Reader; +import java.nio.ByteBuffer; +import javax.websocket.PongMessage; +import javax.websocket.Session; + +import org.eclipse.jetty.websocket.util.InvokerUtils; + +// The different kind of @OnMessage method parameter signatures expected. +public class JavaxWebSocketCallingArgs +{ + static final InvokerUtils.Arg[] textCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(String.class).required() + }; + + static final InvokerUtils.Arg[] textPartialCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(String.class).required(), + new InvokerUtils.Arg(boolean.class).required() + }; + + static final InvokerUtils.Arg[] binaryBufferCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(ByteBuffer.class).required() + }; + + static final InvokerUtils.Arg[] binaryPartialBufferCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(ByteBuffer.class).required(), + new InvokerUtils.Arg(boolean.class).required() + }; + + static final InvokerUtils.Arg[] binaryArrayCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(byte[].class).required() + }; + + static final InvokerUtils.Arg[] binaryPartialArrayCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(byte[].class).required(), + new InvokerUtils.Arg(boolean.class).required() + }; + + static final InvokerUtils.Arg[] inputStreamCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(InputStream.class).required() + }; + + static final InvokerUtils.Arg[] readerCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(Reader.class).required() + }; + + static final InvokerUtils.Arg[] pongCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(PongMessage.class).required() + }; +} diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java index 1ee1797dc869..7ac550cd9e2e 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java @@ -23,6 +23,7 @@ import java.lang.invoke.MethodType; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -46,6 +47,7 @@ import org.eclipse.jetty.websocket.core.exception.ProtocolException; import org.eclipse.jetty.websocket.core.exception.WebSocketException; import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryStreamMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextMessageSink; @@ -95,9 +97,9 @@ public class JavaxWebSocketFrameHandler implements FrameHandler private MethodHandle openHandle; private MethodHandle closeHandle; private MethodHandle errorHandle; - private JavaxWebSocketFrameHandlerMetadata.MessageMetadata textMetadata; - private JavaxWebSocketFrameHandlerMetadata.MessageMetadata binaryMetadata; private MethodHandle pongHandle; + private JavaxWebSocketMessageMetadata textMetadata; + private JavaxWebSocketMessageMetadata binaryMetadata; private UpgradeRequest upgradeRequest; @@ -114,8 +116,8 @@ public class JavaxWebSocketFrameHandler implements FrameHandler public JavaxWebSocketFrameHandler(JavaxWebSocketContainer container, Object endpointInstance, MethodHandle openHandle, MethodHandle closeHandle, MethodHandle errorHandle, - JavaxWebSocketFrameHandlerMetadata.MessageMetadata textMetadata, - JavaxWebSocketFrameHandlerMetadata.MessageMetadata binaryMetadata, + JavaxWebSocketMessageMetadata textMetadata, + JavaxWebSocketMessageMetadata binaryMetadata, MethodHandle pongHandle, EndpointConfig endpointConfig) { @@ -170,26 +172,32 @@ public void onOpen(CoreSession coreSession, Callback callback) errorHandle = InvokerUtils.bindTo(errorHandle, session); pongHandle = InvokerUtils.bindTo(pongHandle, session); - JavaxWebSocketFrameHandlerMetadata.MessageMetadata actualTextMetadata = JavaxWebSocketFrameHandlerMetadata.MessageMetadata.copyOf(textMetadata); + JavaxWebSocketMessageMetadata actualTextMetadata = JavaxWebSocketMessageMetadata.copyOf(textMetadata); if (actualTextMetadata != null) { if (actualTextMetadata.isMaxMessageSizeSet()) - session.setMaxTextMessageBufferSize(actualTextMetadata.maxMessageSize); + session.setMaxTextMessageBufferSize(actualTextMetadata.getMaxMessageSize()); + + MethodHandle methodHandle = actualTextMetadata.getMethodHandle(); + methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); + methodHandle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); + actualTextMetadata.setMethodHandle(methodHandle); - actualTextMetadata.handle = InvokerUtils.bindTo(actualTextMetadata.handle, endpointInstance, endpointConfig, session); - actualTextMetadata.handle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(actualTextMetadata.handle, session); textSink = JavaxWebSocketFrameHandlerFactory.createMessageSink(session, actualTextMetadata); textMetadata = actualTextMetadata; } - JavaxWebSocketFrameHandlerMetadata.MessageMetadata actualBinaryMetadata = JavaxWebSocketFrameHandlerMetadata.MessageMetadata.copyOf(binaryMetadata); + JavaxWebSocketMessageMetadata actualBinaryMetadata = JavaxWebSocketMessageMetadata.copyOf(binaryMetadata); if (actualBinaryMetadata != null) { if (actualBinaryMetadata.isMaxMessageSizeSet()) - session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.maxMessageSize); + session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.getMaxMessageSize()); + + MethodHandle methodHandle = actualBinaryMetadata.getMethodHandle(); + methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); + methodHandle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); + actualBinaryMetadata.setMethodHandle(methodHandle); - actualBinaryMetadata.handle = InvokerUtils.bindTo(actualBinaryMetadata.handle, endpointInstance, endpointConfig, session); - actualBinaryMetadata.handle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(actualBinaryMetadata.handle, session); binarySink = JavaxWebSocketFrameHandlerFactory.createMessageSink(session, actualBinaryMetadata); binaryMetadata = actualBinaryMetadata; } @@ -350,12 +358,12 @@ public Map getMessageHandlerMap() return messageHandlerMap; } - public JavaxWebSocketFrameHandlerMetadata.MessageMetadata getBinaryMetadata() + public JavaxWebSocketMessageMetadata getBinaryMetadata() { return binaryMetadata; } - public JavaxWebSocketFrameHandlerMetadata.MessageMetadata getTextMetadata() + public JavaxWebSocketMessageMetadata getTextMetadata() { return textMetadata; } @@ -369,7 +377,7 @@ private void assertBasicTypeNotRegistered(byte basicWebSocketType, Object messag } } - public void addMessageHandler(JavaxWebSocketSession session, Class clazz, MessageHandler.Partial handler) + public void addMessageHandler(Class clazz, MessageHandler.Partial handler) { try { @@ -384,9 +392,9 @@ public void addMessageHandler(JavaxWebSocketSession session, Class clazz, assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName()); MessageSink messageSink = new PartialByteArrayMessageSink(coreSession, partialMessageHandler); this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink); - JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata(); - metadata.handle = partialMessageHandler; - metadata.sinkClass = PartialByteArrayMessageSink.class; + JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata(); + metadata.setMethodHandle(partialMessageHandler); + metadata.setSinkClass(PartialByteArrayMessageSink.class); this.binaryMetadata = metadata; } else if (ByteBuffer.class.isAssignableFrom(clazz)) @@ -394,9 +402,9 @@ else if (ByteBuffer.class.isAssignableFrom(clazz)) assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName()); MessageSink messageSink = new PartialByteBufferMessageSink(coreSession, partialMessageHandler); this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink); - JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata(); - metadata.handle = partialMessageHandler; - metadata.sinkClass = PartialByteBufferMessageSink.class; + JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata(); + metadata.setMethodHandle(partialMessageHandler); + metadata.setSinkClass(PartialByteBufferMessageSink.class); this.binaryMetadata = metadata; } else if (String.class.isAssignableFrom(clazz)) @@ -404,9 +412,9 @@ else if (String.class.isAssignableFrom(clazz)) assertBasicTypeNotRegistered(OpCode.TEXT, this.textMetadata, handler.getClass().getName()); MessageSink messageSink = new PartialStringMessageSink(coreSession, partialMessageHandler); this.textSink = registerMessageHandler(OpCode.TEXT, clazz, handler, messageSink); - JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata(); - metadata.handle = partialMessageHandler; - metadata.sinkClass = PartialStringMessageSink.class; + JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata(); + metadata.setMethodHandle(partialMessageHandler); + metadata.setSinkClass(PartialStringMessageSink.class); this.textMetadata = metadata; } else @@ -426,67 +434,67 @@ else if (String.class.isAssignableFrom(clazz)) } } - public void addMessageHandler(JavaxWebSocketSession session, Class clazz, MessageHandler.Whole handler) + public void addMessageHandler(Class clazz, MessageHandler.Whole handler) { try { - MethodHandles.Lookup lookup = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup(); - MethodHandle wholeMsgMethodHandle = lookup.findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class)); - wholeMsgMethodHandle = wholeMsgMethodHandle.bindTo(handler); + MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup() + .findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class)) + .bindTo(handler); if (PongMessage.class.isAssignableFrom(clazz)) { assertBasicTypeNotRegistered(OpCode.PONG, this.pongHandle, handler.getClass().getName()); - this.pongHandle = wholeMsgMethodHandle; + this.pongHandle = methodHandle; registerMessageHandler(OpCode.PONG, clazz, handler, null); } else { AvailableDecoders availableDecoders = session.getDecoders(); - AvailableDecoders.RegisteredDecoder registeredDecoder = availableDecoders.getRegisteredDecoderFor(clazz); + RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); if (registeredDecoder == null) { throw new IllegalStateException("Unable to find Decoder for type: " + clazz); } - JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata(); - metadata.handle = wholeMsgMethodHandle; - metadata.registeredDecoder = registeredDecoder; + JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata(); + metadata.setMethodHandle(methodHandle); + metadata.setRegisteredDecoder(registeredDecoder); if (registeredDecoder.implementsInterface(Decoder.Binary.class)) { assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName()); - Decoder.Binary decoder = availableDecoders.getInstanceOf(registeredDecoder); - MessageSink messageSink = new DecodedBinaryMessageSink(coreSession, decoder, wholeMsgMethodHandle); - metadata.sinkClass = messageSink.getClass(); + List binaryDecoders = availableDecoders.getBinaryDecoders(clazz); + MessageSink messageSink = new DecodedBinaryMessageSink(coreSession, methodHandle, binaryDecoders); + metadata.setSinkClass(messageSink.getClass()); this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink); this.binaryMetadata = metadata; } else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) { assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName()); - Decoder.BinaryStream decoder = availableDecoders.getInstanceOf(registeredDecoder); - MessageSink messageSink = new DecodedBinaryStreamMessageSink(coreSession, decoder, wholeMsgMethodHandle); - metadata.sinkClass = messageSink.getClass(); + List binaryStreamDecoders = availableDecoders.getBinaryStreamDecoders(clazz); + MessageSink messageSink = new DecodedBinaryStreamMessageSink(coreSession, methodHandle, binaryStreamDecoders); + metadata.setSinkClass(messageSink.getClass()); this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink); this.binaryMetadata = metadata; } else if (registeredDecoder.implementsInterface(Decoder.Text.class)) { assertBasicTypeNotRegistered(OpCode.TEXT, this.textMetadata, handler.getClass().getName()); - Decoder.Text decoder = availableDecoders.getInstanceOf(registeredDecoder); - MessageSink messageSink = new DecodedTextMessageSink(coreSession, decoder, wholeMsgMethodHandle); - metadata.sinkClass = messageSink.getClass(); + List textDecoders = availableDecoders.getTextDecoders(clazz); + MessageSink messageSink = new DecodedTextMessageSink(coreSession, methodHandle, textDecoders); + metadata.setSinkClass(messageSink.getClass()); this.textSink = registerMessageHandler(OpCode.TEXT, clazz, handler, messageSink); this.textMetadata = metadata; } else if (registeredDecoder.implementsInterface(Decoder.TextStream.class)) { assertBasicTypeNotRegistered(OpCode.TEXT, this.textMetadata, handler.getClass().getName()); - Decoder.TextStream decoder = availableDecoders.getInstanceOf(registeredDecoder); - MessageSink messageSink = new DecodedTextStreamMessageSink(coreSession, decoder, wholeMsgMethodHandle); - metadata.sinkClass = messageSink.getClass(); + List textStreamDecoders = availableDecoders.getTextStreamDecoders(clazz); + MessageSink messageSink = new DecodedTextStreamMessageSink(coreSession, methodHandle, textStreamDecoders); + metadata.setSinkClass(messageSink.getClass()); this.textSink = registerMessageHandler(OpCode.TEXT, clazz, handler, messageSink); this.textMetadata = metadata; } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java index eb51e98d4fa8..1a3da2976b40 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.websocket.javax.common; -import java.io.InputStream; -import java.io.Reader; import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -27,11 +25,11 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.stream.Stream; import javax.websocket.CloseReason; import javax.websocket.Decoder; import javax.websocket.EndpointConfig; @@ -39,15 +37,14 @@ import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; -import javax.websocket.PongMessage; import javax.websocket.Session; import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec; import org.eclipse.jetty.websocket.core.CoreSession; -import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; +import org.eclipse.jetty.websocket.javax.common.messages.AbstractDecodedMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryStreamMessageSink; -import org.eclipse.jetty.websocket.javax.common.messages.DecodedMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextStreamMessageSink; import org.eclipse.jetty.websocket.util.InvalidSignatureException; @@ -65,61 +62,11 @@ import org.eclipse.jetty.websocket.util.messages.StringMessageSink; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerMetadata.MessageMetadata; public abstract class JavaxWebSocketFrameHandlerFactory { private static final MethodHandle FILTER_RETURN_TYPE_METHOD; - // The different kind of @OnMessage method parameter signatures expected. - private static final InvokerUtils.Arg[] textCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(String.class).required() - }; - - private static final InvokerUtils.Arg[] textPartialCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(String.class).required(), - new InvokerUtils.Arg(boolean.class).required() - }; - - private static final InvokerUtils.Arg[] binaryBufferCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(ByteBuffer.class).required() - }; - - private static final InvokerUtils.Arg[] binaryPartialBufferCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(ByteBuffer.class).required(), - new InvokerUtils.Arg(boolean.class).required() - }; - - private static final InvokerUtils.Arg[] binaryArrayCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(byte[].class).required() - }; - - private static final InvokerUtils.Arg[] binaryPartialArrayCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(byte[].class).required(), - new InvokerUtils.Arg(boolean.class).required() - }; - - private static final InvokerUtils.Arg[] inputStreamCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(InputStream.class).required() - }; - - private static final InvokerUtils.Arg[] readerCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(Reader.class).required() - }; - - private static final InvokerUtils.Arg[] pongCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(PongMessage.class).required() - }; - static { try @@ -172,8 +119,8 @@ public JavaxWebSocketFrameHandler newJavaxWebSocketFrameHandler(Object endpointI MethodHandle errorHandle = metadata.getErrorHandle(); MethodHandle pongHandle = metadata.getPongHandle(); - MessageMetadata textMetadata = MessageMetadata.copyOf(metadata.getTextMetadata()); - MessageMetadata binaryMetadata = MessageMetadata.copyOf(metadata.getBinaryMetadata()); + JavaxWebSocketMessageMetadata textMetadata = JavaxWebSocketMessageMetadata.copyOf(metadata.getTextMetadata()); + JavaxWebSocketMessageMetadata binaryMetadata = JavaxWebSocketMessageMetadata.copyOf(metadata.getBinaryMetadata()); UriTemplatePathSpec templatePathSpec = metadata.getUriTemplatePathSpec(); if (templatePathSpec != null) @@ -188,9 +135,9 @@ public JavaxWebSocketFrameHandler newJavaxWebSocketFrameHandler(Object endpointI pongHandle = bindTemplateVariables(pongHandle, namedVariables, pathParams); if (textMetadata != null) - textMetadata.handle = bindTemplateVariables(textMetadata.handle, namedVariables, pathParams); + textMetadata.setMethodHandle(bindTemplateVariables(textMetadata.getMethodHandle(), namedVariables, pathParams)); if (binaryMetadata != null) - binaryMetadata.handle = bindTemplateVariables(binaryMetadata.handle, namedVariables, pathParams); + binaryMetadata.setMethodHandle(bindTemplateVariables(binaryMetadata.getMethodHandle(), namedVariables, pathParams)); } openHandle = InvokerUtils.bindTo(openHandle, endpoint); @@ -313,8 +260,7 @@ else if (Byte.TYPE.isAssignableFrom(type)) return retHandle; } - @SuppressWarnings("Duplicates") - public static MessageSink createMessageSink(JavaxWebSocketSession session, MessageMetadata msgMetadata) + public static MessageSink createMessageSink(JavaxWebSocketSession session, JavaxWebSocketMessageMetadata msgMetadata) { if (msgMetadata == null) return null; @@ -322,27 +268,27 @@ public static MessageSink createMessageSink(JavaxWebSocketSession session, Messa try { MethodHandles.Lookup lookup = getServerMethodHandleLookup(); - if (DecodedMessageSink.class.isAssignableFrom(msgMetadata.sinkClass)) + if (AbstractDecodedMessageSink.class.isAssignableFrom(msgMetadata.getSinkClass())) { - MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.sinkClass, - MethodType.methodType(void.class, CoreSession.class, msgMetadata.registeredDecoder.interfaceType, MethodHandle.class)); - Decoder decoder = session.getDecoders().getInstanceOf(msgMetadata.registeredDecoder); - return (MessageSink)ctorHandle.invoke(session.getCoreSession(), decoder, msgMetadata.handle); + MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), + MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, List.class)); + List registeredDecoders = msgMetadata.getRegisteredDecoders(); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle(), registeredDecoders); } else { - MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.sinkClass, + MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), MethodType.methodType(void.class, CoreSession.class, MethodHandle.class)); - return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.handle); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle()); } } catch (NoSuchMethodException e) { - throw new RuntimeException("Missing expected MessageSink constructor found at: " + msgMetadata.sinkClass.getName(), e); + throw new RuntimeException("Missing expected MessageSink constructor found at: " + msgMetadata.getSinkClass().getName(), e); } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - throw new RuntimeException("Unable to create MessageSink: " + msgMetadata.sinkClass.getName(), e); + throw new RuntimeException("Unable to create MessageSink: " + msgMetadata.getSinkClass().getName(), e); } catch (RuntimeException e) { @@ -456,15 +402,19 @@ protected JavaxWebSocketFrameHandlerMetadata discoverJavaxFrameHandlerMetadata(C for (Method onMsg : onMessages) { assertSignatureValid(endpointClass, onMsg, OnMessage.class); - - MessageMetadata msgMetadata = new MessageMetadata(); OnMessage onMessageAnno = onMsg.getAnnotation(OnMessage.class); - if (onMessageAnno.maxMessageSize() > Integer.MAX_VALUE) + + long annotationMaxMessageSize = onMessageAnno.maxMessageSize(); + if (annotationMaxMessageSize > Integer.MAX_VALUE) { throw new InvalidWebSocketException(String.format("Value too large: %s#%s - @OnMessage.maxMessageSize=%,d > Integer.MAX_VALUE", - endpointClass.getName(), onMsg.getName(), onMessageAnno.maxMessageSize())); + endpointClass.getName(), onMsg.getName(), annotationMaxMessageSize)); } - msgMetadata.maxMessageSize = (int)onMessageAnno.maxMessageSize(); + + // Create MessageMetadata and set annotated maxMessageSize if it is not the default value. + JavaxWebSocketMessageMetadata msgMetadata = new JavaxWebSocketMessageMetadata(); + if (annotationMaxMessageSize != -1) + msgMetadata.setMaxMessageSize((int)annotationMaxMessageSize); // Function to search for matching MethodHandle for the endpointClass given a signature. Function getMethodHandle = (signature) -> @@ -486,91 +436,91 @@ protected JavaxWebSocketFrameHandlerMetadata discoverJavaxFrameHandlerMetadata(C return metadata; } - private boolean matchOnMessage(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, MessageMetadata msgMetadata, + private boolean matchOnMessage(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, JavaxWebSocketMessageMetadata msgMetadata, Function getMethodHandle) { // Whole Text Message. - MethodHandle methodHandle = getMethodHandle.apply(textCallingArgs); + MethodHandle methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.textCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = StringMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(StringMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setTextMetadata(msgMetadata, onMsg); return true; } // Partial Text Message. - methodHandle = getMethodHandle.apply(textPartialCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.textPartialCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = PartialStringMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(PartialStringMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setTextMetadata(msgMetadata, onMsg); return true; } // Whole ByteBuffer Binary Message. - methodHandle = getMethodHandle.apply(binaryBufferCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryBufferCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = ByteBufferMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(ByteBufferMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } // Partial ByteBuffer Binary Message. - methodHandle = getMethodHandle.apply(binaryPartialBufferCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryPartialBufferCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = PartialByteBufferMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(PartialByteBufferMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } // Whole byte[] Binary Message. - methodHandle = getMethodHandle.apply(binaryArrayCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryArrayCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = ByteArrayMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(ByteArrayMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } // Partial byte[] Binary Message. - methodHandle = getMethodHandle.apply(binaryPartialArrayCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryPartialArrayCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = PartialByteArrayMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(PartialByteArrayMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } // InputStream Binary Message. - methodHandle = getMethodHandle.apply(inputStreamCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.inputStreamCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = InputStreamMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(InputStreamMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } // Reader Text Message. - methodHandle = getMethodHandle.apply(readerCallingArgs); + methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.readerCallingArgs); if (methodHandle != null) { - msgMetadata.sinkClass = ReaderMessageSink.class; - msgMetadata.handle = methodHandle; + msgMetadata.setSinkClass(ReaderMessageSink.class); + msgMetadata.setMethodHandle(methodHandle); metadata.setTextMetadata(msgMetadata, onMsg); return true; } // Pong Message. - MethodHandle pongHandle = getMethodHandle.apply(pongCallingArgs); + MethodHandle pongHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.pongCallingArgs); if (pongHandle != null) { metadata.setPongHandle(pongHandle, onMsg); @@ -580,88 +530,70 @@ private boolean matchOnMessage(Method onMsg, JavaxWebSocketFrameHandlerMetadata return false; } - private boolean matchDecoders(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, MessageMetadata msgMetadata, + private boolean matchDecoders(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, JavaxWebSocketMessageMetadata msgMetadata, Function getMethodHandle) { - // TODO: we should be able to get this information directly from the AvailableDecoders in the metadata. - List decodedTextCallingArgs = new ArrayList<>(); - List decodedTextStreamCallingArgs = new ArrayList<>(); - List decodedBinaryCallingArgs = new ArrayList<>(); - List decodedBinaryStreamCallingArgs = new ArrayList<>(); - for (AvailableDecoders.RegisteredDecoder decoder : metadata.getAvailableDecoders()) + // We need to get all the decoders which match not just the first. + Stream matchedDecodersStream = metadata.getAvailableDecoders().stream().filter(registeredDecoder -> { - InvokerUtils.Arg[] args = {new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(decoder.objectType).required()}; - DecodedArgs decodedArgs = new DecodedArgs(decoder, args); + InvokerUtils.Arg[] args = {new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(registeredDecoder.objectType).required()}; + return getMethodHandle.apply(args) != null; + }); - if (decoder.implementsInterface(Decoder.Text.class)) - decodedTextCallingArgs.add(decodedArgs); - if (decoder.implementsInterface(Decoder.TextStream.class)) - decodedTextStreamCallingArgs.add(decodedArgs); - if (decoder.implementsInterface(Decoder.Binary.class)) - decodedBinaryCallingArgs.add(decodedArgs); - if (decoder.implementsInterface(Decoder.BinaryStream.class)) - decodedBinaryStreamCallingArgs.add(decodedArgs); - } + // Use the interface type of the first matched decoder. + RegisteredDecoder firstDecoder = matchedDecodersStream.findFirst().orElse(null); + if (firstDecoder == null) + return false; - MethodHandle methodHandle; - // Decoder.Text - for (DecodedArgs decodedArgs : decodedTextCallingArgs) + // TODO: COMMENT + List decoders = new ArrayList<>(); + Class interfaceType = firstDecoder.interfaceType; + metadata.getAvailableDecoders().stream() + .filter(registeredDecoder -> registeredDecoder.interfaceType.equals(interfaceType)) + .forEach(decoders::add); + + + // Get the original argument type. + Class type = firstDecoder.objectType; + for (Class clazz : onMsg.getParameterTypes()) { - methodHandle = getMethodHandle.apply(decodedArgs.args); - if (methodHandle != null) - { - msgMetadata.sinkClass = DecodedTextMessageSink.class; - msgMetadata.handle = methodHandle; - msgMetadata.registeredDecoder = decodedArgs.registeredDecoder; - metadata.setTextMetadata(msgMetadata, onMsg); - return true; - } + if (clazz.isAssignableFrom(firstDecoder.objectType)) + type = clazz; } - - // Decoder.Binary - for (DecodedArgs decodedArgs : decodedBinaryCallingArgs) + InvokerUtils.Arg[] generalArgs = {new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(type).required()}; + MethodHandle generalMethodHandle = getMethodHandle.apply(generalArgs); + if (generalMethodHandle == null) { - methodHandle = getMethodHandle.apply(decodedArgs.args); - if (methodHandle != null) - { - msgMetadata.sinkClass = DecodedBinaryMessageSink.class; - msgMetadata.handle = methodHandle; - msgMetadata.registeredDecoder = decodedArgs.registeredDecoder; - metadata.setBinaryMetadata(msgMetadata, onMsg); - return true; - } + // TODO: warn or throw + return false; } - // Try to match Text Stream decoders. - for (DecodedArgs decodedArgs : decodedTextStreamCallingArgs) + msgMetadata.setRegisteredDecoders(decoders); + msgMetadata.setMethodHandle(generalMethodHandle); + + if (interfaceType.equals(Decoder.Text.class)) { - methodHandle = getMethodHandle.apply(decodedArgs.args); - if (methodHandle != null) - { - msgMetadata.sinkClass = DecodedTextStreamMessageSink.class; - msgMetadata.handle = methodHandle; - msgMetadata.registeredDecoder = decodedArgs.registeredDecoder; - metadata.setTextMetadata(msgMetadata, onMsg); - return true; - } + msgMetadata.setSinkClass(DecodedTextMessageSink.class); + metadata.setTextMetadata(msgMetadata, onMsg); } - - // Decoder.BinaryStream - for (DecodedArgs decodedArgs : decodedBinaryStreamCallingArgs) + else if (interfaceType.equals(Decoder.Binary.class)) { - methodHandle = getMethodHandle.apply(decodedArgs.args); - if (methodHandle != null) - { - msgMetadata.sinkClass = DecodedBinaryStreamMessageSink.class; - msgMetadata.handle = methodHandle; - msgMetadata.registeredDecoder = decodedArgs.registeredDecoder; - metadata.setBinaryMetadata(msgMetadata, onMsg); - return true; - } + msgMetadata.setSinkClass(DecodedBinaryMessageSink.class); + metadata.setBinaryMetadata(msgMetadata, onMsg); + } + else if (interfaceType.equals(Decoder.TextStream.class)) + { + msgMetadata.setSinkClass(DecodedTextStreamMessageSink.class); + metadata.setTextMetadata(msgMetadata, onMsg); + } + else if (interfaceType.equals(Decoder.BinaryStream.class)) + { + msgMetadata.setSinkClass(DecodedBinaryStreamMessageSink.class); + metadata.setBinaryMetadata(msgMetadata, onMsg); } - return false; + return true; } private void assertSignatureValid(Class endpointClass, Method method, Class annotationClass) @@ -752,16 +684,4 @@ public static MethodHandles.Lookup getApplicationMethodHandleLookup(Class loo { return MethodHandles.publicLookup().in(lookupClass); } - - private static class DecodedArgs - { - public final AvailableDecoders.RegisteredDecoder registeredDecoder; - public final InvokerUtils.Arg[] args; - - public DecodedArgs(AvailableDecoders.RegisteredDecoder registeredDecoder, InvokerUtils.Arg... args) - { - this.registeredDecoder = registeredDecoder; - this.args = args; - } - } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerMetadata.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerMetadata.java index b5db28e9969d..52d96b780265 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerMetadata.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerMetadata.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.websocket.javax.common; import java.lang.invoke.MethodHandle; -import javax.websocket.Decoder; import javax.websocket.Encoder; import javax.websocket.EndpointConfig; @@ -27,25 +26,21 @@ import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders; import org.eclipse.jetty.websocket.util.InvalidWebSocketException; -import org.eclipse.jetty.websocket.util.messages.MessageSink; public class JavaxWebSocketFrameHandlerMetadata { private static final String[] NO_VARIABLES = new String[0]; // EndpointConfig entries - private final EndpointConfig endpointConfig; private final AvailableDecoders availableDecoders; private final AvailableEncoders availableEncoders; private MethodHandle openHandle; private MethodHandle closeHandle; private MethodHandle errorHandle; - - private MessageMetadata textMetadata; - private MessageMetadata binaryMetadata; - private MethodHandle pongHandle; + private JavaxWebSocketMessageMetadata textMetadata; + private JavaxWebSocketMessageMetadata binaryMetadata; /** * For {@code @ServerEndpoint} or {@code ServerEndpointConfig} based endpoints, this @@ -76,7 +71,6 @@ public class JavaxWebSocketFrameHandlerMetadata public JavaxWebSocketFrameHandlerMetadata(EndpointConfig endpointConfig) { - this.endpointConfig = endpointConfig; this.availableDecoders = new AvailableDecoders(endpointConfig); this.availableEncoders = new AvailableEncoders(endpointConfig); } @@ -91,7 +85,7 @@ public AvailableEncoders getAvailableEncoders() return availableEncoders; } - public MessageMetadata getBinaryMetadata() + public JavaxWebSocketMessageMetadata getBinaryMetadata() { return binaryMetadata; } @@ -133,7 +127,7 @@ public MethodHandle getPongHandle() return pongHandle; } - public MessageMetadata getTextMetadata() + public JavaxWebSocketMessageMetadata getTextMetadata() { return textMetadata; } @@ -148,7 +142,7 @@ public boolean hasTextMetdata() return (textMetadata != null); } - public void setBinaryMetadata(MessageMetadata metadata, Object origin) + public void setBinaryMetadata(JavaxWebSocketMessageMetadata metadata, Object origin) { assertNotSet(this.binaryMetadata, "BINARY Message Metadata", origin); this.binaryMetadata = metadata; @@ -160,11 +154,6 @@ public void setCloseHandler(MethodHandle close, Object origin) this.closeHandle = close; } - public void setDecoders(Class[] decoders) - { - this.availableDecoders.registerAll(decoders); - } - public void setEncoders(Class[] encoders) { this.availableEncoders.registerAll(encoders); @@ -188,7 +177,7 @@ public void setPongHandle(MethodHandle pong, Object origin) this.pongHandle = pong; } - public void setTextMetadata(MessageMetadata metadata, Object origin) + public void setTextMetadata(JavaxWebSocketMessageMetadata metadata, Object origin) { assertNotSet(this.textMetadata, "TEXT Messsage Metadata", origin); this.textMetadata = metadata; @@ -219,33 +208,4 @@ private String describeOrigin(Object obj) return obj.toString(); } - - public static class MessageMetadata - { - private static final int UNSET = -1; - - public MethodHandle handle; - public Class sinkClass; - public AvailableDecoders.RegisteredDecoder registeredDecoder; - public int maxMessageSize = UNSET; - - public static MessageMetadata copyOf(MessageMetadata metadata) - { - if (metadata == null) - return null; - - MessageMetadata copy = new MessageMetadata(); - copy.handle = metadata.handle; - copy.sinkClass = metadata.sinkClass; - copy.registeredDecoder = metadata.registeredDecoder; - copy.maxMessageSize = metadata.maxMessageSize; - - return copy; - } - - public boolean isMaxMessageSizeSet() - { - return maxMessageSize != UNSET; - } - } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketMessageMetadata.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketMessageMetadata.java new file mode 100644 index 000000000000..f73f2e69a0a6 --- /dev/null +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketMessageMetadata.java @@ -0,0 +1,115 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.javax.common; + +import java.lang.invoke.MethodHandle; +import java.util.List; + +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; +import org.eclipse.jetty.websocket.util.messages.MessageSink; + +public class JavaxWebSocketMessageMetadata +{ + public static enum Type + { + TEXT, + BINARY, + TEXT_STREAM, + BINARY_STREAM + } + + private MethodHandle methodHandle; + private Class sinkClass; + private List registeredDecoders; + + private int maxMessageSize = -1; + private boolean maxMessageSizeSet = false; + + public static JavaxWebSocketMessageMetadata copyOf(JavaxWebSocketMessageMetadata metadata) + { + if (metadata == null) + return null; + + JavaxWebSocketMessageMetadata copy = new JavaxWebSocketMessageMetadata(); + copy.methodHandle = metadata.methodHandle; + copy.sinkClass = metadata.sinkClass; + copy.registeredDecoders = metadata.registeredDecoders; + copy.maxMessageSize = metadata.maxMessageSize; + copy.maxMessageSizeSet = metadata.maxMessageSizeSet; + return copy; + } + + public boolean isMaxMessageSizeSet() + { + return maxMessageSizeSet; + } + + public int getMaxMessageSize() + { + return maxMessageSize; + } + + public void setMaxMessageSize(int maxMessageSize) + { + this.maxMessageSize = maxMessageSize; + this.maxMessageSizeSet = true; + } + + public MethodHandle getMethodHandle() + { + return methodHandle; + } + + public void setMethodHandle(MethodHandle methodHandle) + { + this.methodHandle = methodHandle; + } + + public Class getSinkClass() + { + return sinkClass; + } + + public void setSinkClass(Class sinkClass) + { + this.sinkClass = sinkClass; + } + + public RegisteredDecoder getRegisteredDecoder() + { + if (registeredDecoders == null || registeredDecoders.isEmpty()) + return null; + return registeredDecoders.get(0); + } + + public void setRegisteredDecoder(RegisteredDecoder registeredDecoder) + { + this.registeredDecoders = List.of(registeredDecoder); + } + + public List getRegisteredDecoders() + { + return registeredDecoders; + } + + public void setRegisteredDecoders(List decoders) + { + this.registeredDecoders = decoders; + } +} diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java index 7c40cd411a2a..6da55c40efeb 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java @@ -59,12 +59,11 @@ public class JavaxWebSocketSession implements javax.websocket.Session private final JavaxWebSocketContainer container; private final CoreSession coreSession; private final JavaxWebSocketFrameHandler frameHandler; - private final EndpointConfig config; private final AvailableDecoders availableDecoders; private final AvailableEncoders availableEncoders; private final Map pathParameters; private final String sessionId; - private Map userProperties; + private final Map userProperties; private List negotiatedExtensions; private JavaxWebSocketAsyncRemote asyncRemote; @@ -75,17 +74,17 @@ public JavaxWebSocketSession(JavaxWebSocketContainer container, JavaxWebSocketFrameHandler frameHandler, EndpointConfig endpointConfig) { + Objects.requireNonNull(endpointConfig); this.container = container; this.coreSession = coreSession; this.frameHandler = frameHandler; this.sessionId = UUID.randomUUID().toString(); - this.config = Objects.requireNonNull(endpointConfig); - this.availableDecoders = new AvailableDecoders(this.config); - this.availableEncoders = new AvailableEncoders(this.config); + this.availableDecoders = new AvailableDecoders(endpointConfig); + this.availableEncoders = new AvailableEncoders(endpointConfig); - if (this.config instanceof PathParamProvider) + if (endpointConfig instanceof PathParamProvider) { - PathParamProvider pathParamProvider = (PathParamProvider)this.config; + PathParamProvider pathParamProvider = (PathParamProvider)endpointConfig; this.pathParameters = new HashMap<>(pathParamProvider.getPathParams()); } else @@ -93,7 +92,7 @@ public JavaxWebSocketSession(JavaxWebSocketContainer container, this.pathParameters = Collections.emptyMap(); } - this.userProperties = this.config.getUserProperties(); + this.userProperties = endpointConfig.getUserProperties(); } public CoreSession getCoreSession() @@ -116,7 +115,7 @@ public void addMessageHandler(Class clazz, MessageHandler.Partial hand LOG.debug("Add MessageHandler.Partial: {}", handler); } - frameHandler.addMessageHandler(this, clazz, handler); + frameHandler.addMessageHandler(clazz, handler); } /** @@ -134,7 +133,7 @@ public void addMessageHandler(Class clazz, MessageHandler.Whole handle LOG.debug("Add MessageHandler.Whole: {}", handler); } - frameHandler.addMessageHandler(this, clazz, handler); + frameHandler.addMessageHandler(clazz, handler); } /** diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/AvailableDecoders.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/AvailableDecoders.java index 51703c578245..a1f0e4f4531a 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/AvailableDecoders.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/AvailableDecoders.java @@ -20,82 +20,28 @@ import java.io.InputStream; import java.io.Reader; -import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.websocket.Decoder; import javax.websocket.EndpointConfig; -import org.eclipse.jetty.websocket.javax.common.InitException; import org.eclipse.jetty.websocket.util.InvalidSignatureException; import org.eclipse.jetty.websocket.util.InvalidWebSocketException; import org.eclipse.jetty.websocket.util.ReflectUtils; -public class AvailableDecoders implements Iterable +public class AvailableDecoders implements Iterable { - public static class RegisteredDecoder - { - // The user supplied Decoder class - public final Class decoder; - // The javax.websocket.Decoder.* type (eg: Decoder.Binary, Decoder.BinaryStream, Decoder.Text, Decoder.TextStream) - public final Class interfaceType; - public final Class objectType; - public final boolean primitive; - public Decoder instance; - - public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType) - { - this(decoder, interfaceType, objectType, false); - } - - public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, boolean primitive) - { - this.decoder = decoder; - this.interfaceType = interfaceType; - this.objectType = objectType; - this.primitive = primitive; - } - - public boolean implementsInterface(Class type) - { - return interfaceType.isAssignableFrom(type); - } - - public boolean isType(Class type) - { - return objectType.isAssignableFrom(type); - } - - @Override - public String toString() - { - StringBuilder str = new StringBuilder(); - str.append(RegisteredDecoder.class.getSimpleName()); - str.append('[').append(decoder.getName()); - str.append(',').append(interfaceType.getName()); - str.append(',').append(objectType.getName()); - if (primitive) - { - str.append(",PRIMITIVE"); - } - str.append(']'); - return str.toString(); - } - } - + private final List registeredDecoders = new LinkedList<>(); private final EndpointConfig config; - private LinkedList registeredDecoders; public AvailableDecoders(EndpointConfig config) { - Objects.requireNonNull(config); - this.config = config; - registeredDecoders = new LinkedList<>(); + this.config = Objects.requireNonNull(config); // TEXT based [via Class reference] registerPrimitive(BooleanDecoder.class, Decoder.Text.class, Boolean.class); @@ -132,7 +78,7 @@ public AvailableDecoders(EndpointConfig config) private void registerPrimitive(Class decoderClass, Class interfaceType, Class type) { - registeredDecoders.add(new RegisteredDecoder(decoderClass, interfaceType, type, true)); + registeredDecoders.add(new RegisteredDecoder(decoderClass, interfaceType, type, config, true)); } public void register(Class decoder) @@ -175,23 +121,10 @@ public void register(Class decoder) } } - // TODO: consider removing (if not used) - public void registerAll(Class[] decoders) - { - if (decoders == null) - return; - - for (Class decoder : decoders) - { - register(decoder); - } - } - public void registerAll(List> decoders) { if (decoders == null) return; - decoders.forEach(this::register); } @@ -200,102 +133,59 @@ private void add(Class decoder, Class inte Class objectType = ReflectUtils.findGenericClassFor(decoder, interfaceClass); if (objectType == null) { - StringBuilder err = new StringBuilder(); - err.append("Unknown Decoder Object type declared for interface "); - err.append(interfaceClass.getName()); - err.append(" on class "); - err.append(decoder); - throw new InvalidWebSocketException(err.toString()); + String err = "Unknown Decoder Object type declared for interface " + + interfaceClass.getName() + " on class " + decoder; + throw new InvalidWebSocketException(err); } - try - { - RegisteredDecoder conflicts = registeredDecoders.stream() - .filter(registered -> registered.isType(objectType)) - .filter(registered -> !registered.primitive) - .findFirst() - .get(); + boolean alreadyRegistered = registeredDecoders.stream().anyMatch(registered -> + registered.decoder.equals(decoder) && registered.interfaceType.equals(interfaceClass)); - if (conflicts.decoder.equals(decoder) && conflicts.implementsInterface(interfaceClass)) - { - // Same decoder as what is there already, don't bother adding it again. - return; - } + // If decoder is already registered for this interfaceType, don't bother adding it again. + if (!alreadyRegistered) + registeredDecoders.add(0, new RegisteredDecoder(decoder, interfaceClass, objectType, config)); + } - StringBuilder err = new StringBuilder(); - err.append("Duplicate Decoder Object type "); - err.append(objectType.getName()); - err.append(" in "); - err.append(decoder.getName()); - err.append(", previously declared in "); - err.append(conflicts.decoder.getName()); - throw new InvalidWebSocketException(err.toString()); - } - catch (NoSuchElementException e) - { - registeredDecoders.addFirst(new RegisteredDecoder(decoder, interfaceClass, objectType)); - } + public RegisteredDecoder getFirstRegisteredDecoder(Class type) + { + return registeredDecoders.stream() + .filter(registered -> registered.isType(type)) + .findFirst() + .orElse(null); } - // TODO: consider removing (if not used) - public List supporting(Class interfaceType) + public List getRegisteredDecoders(Class returnType) { return registeredDecoders.stream() - .filter(registered -> registered.implementsInterface(interfaceType)) + .filter(registered -> registered.isType(returnType)) .collect(Collectors.toList()); } - public RegisteredDecoder getRegisteredDecoderFor(Class type) + public List getRegisteredDecoders(Class interfaceType, Class returnType) { return registeredDecoders.stream() - .filter(registered -> registered.isType(type)) - .findFirst() - .orElse(null); + .filter(registered -> registered.interfaceType.equals(interfaceType) && registered.isType(returnType)) + .collect(Collectors.toList()); } - // TODO: consider removing (if not used) - public Class getDecoderFor(Class type) + public List getTextDecoders(Class returnType) { - try - { - return getRegisteredDecoderFor(type).decoder; - } - catch (NoSuchElementException e) - { - throw new InvalidWebSocketException("No Decoder found for type " + type); - } + return getRegisteredDecoders(Decoder.Text.class, returnType); } - public T getInstanceOf(RegisteredDecoder registeredDecoder) + public List getBinaryDecoders(Class returnType) { - if (registeredDecoder.instance != null) - { - return (T)registeredDecoder.instance; - } + return getRegisteredDecoders(Decoder.Binary.class, returnType); + } - try - { - registeredDecoder.instance = registeredDecoder.decoder.getConstructor().newInstance(); - registeredDecoder.instance.init(this.config); - return (T)registeredDecoder.instance; - } - catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) - { - throw new InitException("Unable to init Decoder for type:" + registeredDecoder.decoder.getName(), e); - } + public List getTextStreamDecoders(Class returnType) + { + return getRegisteredDecoders(Decoder.TextStream.class, returnType); } - public T getInstanceFor(Class type) + public List getBinaryStreamDecoders(Class returnType) { - try - { - RegisteredDecoder registeredDecoder = getRegisteredDecoderFor(type); - return getInstanceOf(registeredDecoder); - } - catch (NoSuchElementException e) - { - throw new InvalidWebSocketException("No Decoder found for type " + type); - } + return getRegisteredDecoders(Decoder.BinaryStream.class, returnType); } @Override @@ -303,4 +193,9 @@ public Iterator iterator() { return registeredDecoders.iterator(); } + + public Stream stream() + { + return registeredDecoders.stream(); + } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/RegisteredDecoder.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/RegisteredDecoder.java new file mode 100644 index 000000000000..120923bcb218 --- /dev/null +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/decoders/RegisteredDecoder.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.javax.common.decoders; + +import java.lang.reflect.InvocationTargetException; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; + +import org.eclipse.jetty.websocket.javax.common.InitException; + +public class RegisteredDecoder +{ + // The user supplied Decoder class + public final Class decoder; + // The javax.websocket.Decoder.* type (eg: Decoder.Binary, Decoder.BinaryStream, Decoder.Text, Decoder.TextStream) + public final Class interfaceType; + public final Class objectType; + public final boolean primitive; + public final EndpointConfig config; + + private Decoder instance; + + public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, EndpointConfig endpointConfig) + { + this(decoder, interfaceType, objectType, endpointConfig, false); + } + + public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, EndpointConfig endpointConfig, boolean primitive) + { + this.decoder = decoder; + this.interfaceType = interfaceType; + this.objectType = objectType; + this.primitive = primitive; + this.config = endpointConfig; + } + + public boolean implementsInterface(Class type) + { + return interfaceType.isAssignableFrom(type); + } + + public boolean isType(Class type) + { + return objectType.isAssignableFrom(type); + } + + public T getInstance() + { + if (instance == null) + { + try + { + instance = decoder.getConstructor().newInstance(); + instance.init(config); + return (T)instance; + } + catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) + { + throw new InitException("Unable to init Decoder for type:" + decoder.getName(), e); + } + } + + return (T)instance; + } + + @Override + public String toString() + { + StringBuilder str = new StringBuilder(); + str.append(RegisteredDecoder.class.getSimpleName()); + str.append('[').append(decoder.getName()); + str.append(',').append(interfaceType.getName()); + str.append(',').append(objectType.getName()); + if (primitive) + { + str.append(",PRIMITIVE"); + } + str.append(']'); + return str.toString(); + } +} diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractDecodedMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractDecodedMessageSink.java new file mode 100644 index 000000000000..640db1430019 --- /dev/null +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractDecodedMessageSink.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.javax.common.messages; + +import java.lang.invoke.MethodHandle; +import java.util.List; +import java.util.stream.Collectors; +import javax.websocket.Decoder; + +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; +import org.eclipse.jetty.websocket.util.messages.MessageSink; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractDecodedMessageSink implements MessageSink +{ + protected final Logger _logger; + protected final CoreSession _coreSession; + protected final MethodHandle _methodHandle; + protected final MessageSink _messageSink; + protected final List _decoders; + + public AbstractDecodedMessageSink(CoreSession coreSession, MethodHandle methodHandle, List decoders) + { + _logger = LoggerFactory.getLogger(getClass()); + _coreSession = coreSession; + _methodHandle = methodHandle; + _decoders = decoders.stream() + .map(RegisteredDecoder::getInstance) + .collect(Collectors.toList()); + + try + { + _messageSink = getMessageSink(); + } + catch (Exception e) + { + // Throwing from here is an error implementation of the DecodedMessageSink. + throw new RuntimeException(e); + } + } + + /** + * @return a message sink which will first decode the message then pass it to {@link #_methodHandle}. + * @throws Exception for any error in creating the message sink. + */ + abstract MessageSink getMessageSink() throws Exception; + + @Override + public void accept(Frame frame, Callback callback) + { + if (_logger.isDebugEnabled()) + _logger.debug("accepting frame {} for {}", frame, _messageSink); + _messageSink.accept(frame, callback); + } +} diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java index 7932300479ab..a4fa711336ce 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java @@ -21,6 +21,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteBuffer; +import java.util.List; import javax.websocket.CloseReason; import javax.websocket.DecodeException; import javax.websocket.Decoder; @@ -28,54 +29,48 @@ import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.util.messages.ByteBufferMessageSink; import org.eclipse.jetty.websocket.util.messages.MessageSink; -public class DecodedBinaryMessageSink extends DecodedMessageSink> +public class DecodedBinaryMessageSink extends AbstractDecodedMessageSink> { - public DecodedBinaryMessageSink(CoreSession session, - Decoder.Binary decoder, - MethodHandle methodHandle) - throws NoSuchMethodException, IllegalAccessException + public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) { - super(session, decoder, methodHandle); + super(session, methodHandle, decoders); } @Override - protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException + MessageSink getMessageSink() throws Exception { - return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryMessageSink.class, + MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryMessageSink.class, "onWholeMessage", MethodType.methodType(void.class, ByteBuffer.class)) .bindTo(this); - } - - @Override - protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle) - { - return new ByteBufferMessageSink(session, rawMethodHandle); + return new ByteBufferMessageSink(_coreSession, methodHandle); } @SuppressWarnings("Duplicates") public void onWholeMessage(ByteBuffer wholeMessage) { - if (!getDecoder().willDecode(wholeMessage)) - { - logger.warn("Message lost, decoder " + getDecoder().getClass().getName() + "#willDecode() has rejected it."); - return; - } - - try - { - T obj = getDecoder().decode(wholeMessage); - methodHandle.invoke(obj); - } - catch (DecodeException e) - { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); - } - catch (Throwable t) + for (Decoder.Binary decoder : _decoders) { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + if (decoder.willDecode(wholeMessage)) + { + try + { + T obj = decoder.decode(wholeMessage); + _methodHandle.invoke(obj); + return; + } + catch (DecodeException e) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); + } + catch (Throwable t) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + } + } } } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSink.java index 32a736c4acb4..13bb34b7133c 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSink.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSink.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.util.List; import javax.websocket.CloseReason; import javax.websocket.DecodeException; import javax.websocket.Decoder; @@ -28,48 +29,45 @@ import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.util.messages.InputStreamMessageSink; import org.eclipse.jetty.websocket.util.messages.MessageSink; -public class DecodedBinaryStreamMessageSink extends DecodedMessageSink> +public class DecodedBinaryStreamMessageSink extends AbstractDecodedMessageSink> { - public DecodedBinaryStreamMessageSink(CoreSession session, - Decoder.BinaryStream decoder, - MethodHandle methodHandle) - throws NoSuchMethodException, IllegalAccessException + public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) throws NoSuchMethodException, IllegalAccessException { - super(session, decoder, methodHandle); + super(session, methodHandle, decoders); } @Override - protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException + MessageSink getMessageSink() throws Exception { - return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryStreamMessageSink.class, + MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryStreamMessageSink.class, "onStreamStart", MethodType.methodType(void.class, InputStream.class)) .bindTo(this); - } - - @Override - protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle) - { - return new InputStreamMessageSink(session, rawMethodHandle); + return new InputStreamMessageSink(_coreSession, methodHandle); } @SuppressWarnings("Duplicates") public void onStreamStart(InputStream stream) { - try - { - T obj = getDecoder().decode(stream); - methodHandle.invoke(obj); - } - catch (DecodeException e) - { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); - } - catch (Throwable t) + for (Decoder.BinaryStream decoder : _decoders) { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + try + { + T obj = decoder.decode(stream); + _methodHandle.invoke(obj); + return; + } + catch (DecodeException e) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); + } + catch (Throwable t) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + } } } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedMessageSink.java deleted file mode 100644 index 1c6e195fbaae..000000000000 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedMessageSink.java +++ /dev/null @@ -1,64 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under -// the terms of the Eclipse Public License 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0 -// -// This Source Code may also be made available under the following -// Secondary Licenses when the conditions for such availability set -// forth in the Eclipse Public License, v. 2.0 are satisfied: -// the Apache License v2.0 which is available at -// https://www.apache.org/licenses/LICENSE-2.0 -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.websocket.javax.common.messages; - -import java.lang.invoke.MethodHandle; -import javax.websocket.Decoder; - -import org.eclipse.jetty.util.Callback; -import org.eclipse.jetty.websocket.core.CoreSession; -import org.eclipse.jetty.websocket.core.Frame; -import org.eclipse.jetty.websocket.util.messages.AbstractMessageSink; -import org.eclipse.jetty.websocket.util.messages.MessageSink; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class DecodedMessageSink extends AbstractMessageSink -{ - protected final Logger logger; - private final T decoder; - private final MethodHandle rawMethodHandle; - private final MessageSink rawMessageSink; - - public DecodedMessageSink(CoreSession session, T decoder, MethodHandle methodHandle) - throws NoSuchMethodException, IllegalAccessException - { - super(session, methodHandle); - this.logger = LoggerFactory.getLogger(this.getClass()); - this.decoder = decoder; - this.rawMethodHandle = newRawMethodHandle(); - this.rawMessageSink = newRawMessageSink(session, rawMethodHandle); - } - - protected abstract MethodHandle newRawMethodHandle() - throws NoSuchMethodException, IllegalAccessException; - - protected abstract MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle); - - public T getDecoder() - { - return decoder; - } - - @Override - public void accept(Frame frame, Callback callback) - { - this.rawMessageSink.accept(frame, callback); - } -} diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSink.java index 593e48c945f2..1d91ec1e1d1c 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSink.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSink.java @@ -20,6 +20,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.util.List; import javax.websocket.CloseReason; import javax.websocket.DecodeException; import javax.websocket.Decoder; @@ -27,54 +28,47 @@ import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.util.messages.MessageSink; import org.eclipse.jetty.websocket.util.messages.StringMessageSink; -public class DecodedTextMessageSink extends DecodedMessageSink> +public class DecodedTextMessageSink extends AbstractDecodedMessageSink> { - public DecodedTextMessageSink(CoreSession session, - Decoder.Text decoder, - MethodHandle methodHandle) - throws NoSuchMethodException, IllegalAccessException + public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) throws NoSuchMethodException, IllegalAccessException { - super(session, decoder, methodHandle); + super(session, methodHandle, decoders); } @Override - protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException + MessageSink getMessageSink() throws NoSuchMethodException, IllegalAccessException { - return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextMessageSink.class, - "onWholeMessage", MethodType.methodType(void.class, String.class)) + MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup() + .findVirtual(getClass(), "onMessage", MethodType.methodType(void.class, String.class)) .bindTo(this); + return new StringMessageSink(_coreSession, methodHandle); } - @Override - protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle) - { - return new StringMessageSink(session, rawMethodHandle); - } - - @SuppressWarnings("Duplicates") - public void onWholeMessage(String wholeMessage) + public void onMessage(String wholeMessage) { - if (!getDecoder().willDecode(wholeMessage)) - { - logger.warn("Message lost, decoder " + getDecoder().getClass().getName() + "#willDecode() has rejected it."); - return; - } - - try - { - T obj = getDecoder().decode(wholeMessage); - methodHandle.invoke(obj); - } - catch (DecodeException e) - { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); - } - catch (Throwable t) + for (Decoder.Text decoder : _decoders) { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + if (decoder.willDecode(wholeMessage)) + { + try + { + T obj = decoder.decode(wholeMessage); + _methodHandle.invoke(obj); + return; + } + catch (DecodeException e) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); + } + catch (Throwable t) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + } + } } } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSink.java index 58a51a4a2e13..a5bda1448863 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSink.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSink.java @@ -21,6 +21,7 @@ import java.io.Reader; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.util.List; import javax.websocket.CloseReason; import javax.websocket.DecodeException; import javax.websocket.Decoder; @@ -28,48 +29,44 @@ import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.util.messages.MessageSink; import org.eclipse.jetty.websocket.util.messages.ReaderMessageSink; -public class DecodedTextStreamMessageSink extends DecodedMessageSink> +public class DecodedTextStreamMessageSink extends AbstractDecodedMessageSink> { - public DecodedTextStreamMessageSink(CoreSession session, - Decoder.TextStream decoder, - MethodHandle methodHandle) - throws NoSuchMethodException, IllegalAccessException + public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) throws NoSuchMethodException, IllegalAccessException { - super(session, decoder, methodHandle); + super(session, methodHandle, decoders); } @Override - protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException + MessageSink getMessageSink() throws Exception { - return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextStreamMessageSink.class, + MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextStreamMessageSink.class, "onStreamStart", MethodType.methodType(void.class, Reader.class)) .bindTo(this); + return new ReaderMessageSink(_coreSession, methodHandle); } - @Override - protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle) - { - return new ReaderMessageSink(session, rawMethodHandle); - } - - @SuppressWarnings("Duplicates") public void onStreamStart(Reader reader) { - try - { - T obj = getDecoder().decode(reader); - methodHandle.invoke(obj); - } - catch (DecodeException e) - { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); - } - catch (Throwable t) + for (Decoder.TextStream decoder : _decoders) { - throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + try + { + T obj = decoder.decode(reader); + _methodHandle.invoke(obj); + return; + } + catch (DecodeException e) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e); + } + catch (Throwable t) + { + throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t); + } } } } diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractMessageSinkTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractMessageSinkTest.java index 2965cc662f38..8561c3c79edf 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractMessageSinkTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/AbstractMessageSinkTest.java @@ -20,13 +20,34 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.util.List; import java.util.function.Consumer; +import javax.websocket.ClientEndpointConfig; +import javax.websocket.Decoder; import org.eclipse.jetty.websocket.javax.common.AbstractSessionTest; import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; public abstract class AbstractMessageSinkTest extends AbstractSessionTest { + public List toRegisteredDecoderList(Class clazz, Class objectType) + { + Class interfaceType; + if (Decoder.Text.class.isAssignableFrom(clazz)) + interfaceType = Decoder.Text.class; + else if (Decoder.Binary.class.isAssignableFrom(clazz)) + interfaceType = Decoder.Binary.class; + else if (Decoder.TextStream.class.isAssignableFrom(clazz)) + interfaceType = Decoder.TextStream.class; + else if (Decoder.BinaryStream.class.isAssignableFrom(clazz)) + interfaceType = Decoder.BinaryStream.class; + else + throw new IllegalStateException(); + + return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build())); + } + public MethodHandle getAcceptHandle(Consumer copy, Class type) { try diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSinkTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSinkTest.java index afa3f919c732..416b3aece9f7 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSinkTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSinkTest.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Calendar; +import java.util.List; import java.util.TimeZone; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -34,6 +35,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.javax.common.AbstractSessionTest; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -49,8 +51,8 @@ public void testCalendar1Frame() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); - Decoder.Binary decoder = new GmtDecoder(); - DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink(AbstractSessionTest.session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); + DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders); FutureCallback finCallback = new FutureCallback(); ByteBuffer data = ByteBuffer.allocate(16); @@ -72,8 +74,8 @@ public void testCalendar3Frames() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); - Decoder.Binary decoder = new GmtDecoder(); - DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink(AbstractSessionTest.session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); + DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSinkTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSinkTest.java index 1fbd4cf03d35..dea38cd9fc0a 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSinkTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryStreamMessageSinkTest.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Calendar; +import java.util.List; import java.util.TimeZone; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -36,6 +37,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,8 +53,8 @@ public void testCalendar1Frame() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); - Decoder.BinaryStream decoder = new GmtDecoder(); - DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink(session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); + DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); FutureCallback finCallback = new FutureCallback(); ByteBuffer data = ByteBuffer.allocate(16); @@ -74,8 +76,8 @@ public void testCalendar3Frames() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); - Decoder.BinaryStream decoder = new GmtDecoder(); - DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink(session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); + DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSinkTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSinkTest.java index f805aec78b58..4a4a5e2012a9 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSinkTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextMessageSinkTest.java @@ -21,7 +21,9 @@ import java.lang.invoke.MethodHandle; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; +import java.util.List; import java.util.TimeZone; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -33,6 +35,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -48,8 +51,8 @@ public void testDate1Frame() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); - Decoder.Text decoder = new GmtDecoder(); - DecodedTextMessageSink sink = new DecodedTextMessageSink(session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class); + DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders); FutureCallback finCallback = new FutureCallback(); sink.accept(new Frame(OpCode.TEXT).setPayload("2018.02.13").setFin(true), finCallback); @@ -66,8 +69,8 @@ public void testDate3Frames() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); - Decoder.Text decoder = new GmtDecoder(); - DecodedTextMessageSink sink = new DecodedTextMessageSink(session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class); + DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSinkTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSinkTest.java index d33952b6f942..051a91b45959 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSinkTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedTextStreamMessageSinkTest.java @@ -23,7 +23,9 @@ import java.lang.invoke.MethodHandle; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; +import java.util.List; import java.util.TimeZone; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -36,6 +38,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,8 +54,8 @@ public void testDate1Frame() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); - Decoder.TextStream decoder = new GmtDecoder(); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink(session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); FutureCallback finCallback = new FutureCallback(); sink.accept(new Frame(OpCode.TEXT).setPayload("2018.02.13").setFin(true), finCallback); @@ -69,8 +72,8 @@ public void testDate3Frames() throws Exception CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); - Decoder.TextStream decoder = new GmtDecoder(); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink(session.getCoreSession(), decoder, copyHandle); + List decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerFrameHandlerFactory.java b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerFrameHandlerFactory.java index bb81bbc3704d..4f6c29a38f4c 100644 --- a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerFrameHandlerFactory.java +++ b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerFrameHandlerFactory.java @@ -42,15 +42,11 @@ public JavaxWebSocketServerFrameHandlerFactory(JavaxWebSocketContainer container public JavaxWebSocketFrameHandlerMetadata getMetadata(Class endpointClass, EndpointConfig endpointConfig) { if (javax.websocket.Endpoint.class.isAssignableFrom(endpointClass)) - { return createEndpointMetadata((Class)endpointClass, endpointConfig); - } ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class); if (anno == null) - { return super.getMetadata(endpointClass, endpointConfig); - } UriTemplatePathSpec templatePathSpec = new UriTemplatePathSpec(anno.value()); JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig); diff --git a/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerType.java b/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerType.java index 325d5fd71a1e..bc77695fc28e 100644 --- a/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerType.java +++ b/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerType.java @@ -23,7 +23,7 @@ import javax.websocket.PongMessage; import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketSession; -import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.javax.tests.MessageType; import org.eclipse.jetty.websocket.util.ReflectUtils; import org.hamcrest.Description; @@ -78,7 +78,7 @@ else if (MessageHandler.Partial.class.isAssignableFrom(handlerClass)) return false; } - AvailableDecoders.RegisteredDecoder registeredDecoder = session.getDecoders().getRegisteredDecoderFor(onMessageClass); + RegisteredDecoder registeredDecoder = session.getDecoders().getFirstRegisteredDecoder(onMessageClass); if (registeredDecoder == null) { return false; diff --git a/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerTypeRegistered.java b/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerTypeRegistered.java index 4b89d83771f5..fd67cbab4103 100644 --- a/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerTypeRegistered.java +++ b/jetty-websocket/websocket-javax-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/matchers/IsMessageHandlerTypeRegistered.java @@ -25,7 +25,7 @@ import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketSession; import org.eclipse.jetty.websocket.javax.common.RegisteredMessageHandler; -import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.javax.tests.MessageType; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; @@ -60,7 +60,7 @@ protected boolean matchesSafely(JavaxWebSocketSession session) { Class onMessageType = registeredMessageHandler.getHandlerType(); - AvailableDecoders.RegisteredDecoder registeredDecoder = session.getDecoders().getRegisteredDecoderFor(onMessageType); + RegisteredDecoder registeredDecoder = session.getDecoders().getFirstRegisteredDecoder(onMessageType); if (registeredDecoder == null) { continue; @@ -130,7 +130,7 @@ protected void describeMismatchSafely(JavaxWebSocketSession session, Description mismatchDescription.appendText("<" + onMessageType.getName() + ">"); - AvailableDecoders.RegisteredDecoder registeredDecoder = session.getDecoders().getRegisteredDecoderFor(onMessageType); + RegisteredDecoder registeredDecoder = session.getDecoders().getFirstRegisteredDecoder(onMessageType); if (registeredDecoder == null) { mismatchDescription.appendText("(!NO-DECODER!)"); diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/DecoderListTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/DecoderListTest.java new file mode 100644 index 000000000000..9dd215353574 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/DecoderListTest.java @@ -0,0 +1,162 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.javax.tests; + +import java.net.URI; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; +import javax.websocket.OnMessage; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.websocket.javax.client.internal.JavaxWebSocketClientContainer; +import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class DecoderListTest +{ + private Server server; + private URI serverUri; + private JavaxWebSocketClientContainer client; + + @BeforeEach + public void before() throws Exception + { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + server.addConnector(connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setContextPath("/"); + server.setHandler(contextHandler); + JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> + container.addEndpoint(DecoderListEndpoint.class)); + server.start(); + serverUri = WSURI.toWebsocket(server.getURI()); + + client = new JavaxWebSocketClientContainer(); + client.start(); + } + + @AfterEach + public void after() throws Exception + { + server.stop(); + client.stop(); + } + + public static Stream getArguments() + { + return Stream.of( + Arguments.of("=DecodeEquals", "DecodeEquals="), + Arguments.of("+DecodePlus", "DecodePlus+"), + Arguments.of("-DecodeMinus", "DecodeMinus-"), + Arguments.of("DecodeNoMatch", null) + ); + } + + @ParameterizedTest + @MethodSource("getArguments") + public void testDecoderList(String request, String expected) throws Exception + { + EventSocket clientEndpoint = new EventSocket(); + Session session = client.connectToServer(clientEndpoint, serverUri); + session.getBasicRemote().sendText(request); + String response = clientEndpoint.textMessages.poll(3, TimeUnit.SECONDS); + assertThat(response, is(expected)); + } + + @ServerEndpoint(value = "/", decoders = {EqualsDecoder.class, PlusDecoder.class, MinusDecoder.class}) + public static class DecoderListEndpoint + { + @OnMessage + public String echo(String message) + { + return message; + } + } + + public static class EqualsDecoder extends PrefixStringDecoder + { + public EqualsDecoder() + { + super("="); + } + } + + public static class PlusDecoder extends PrefixStringDecoder + { + public PlusDecoder() + { + super("+"); + } + } + + public static class MinusDecoder extends PrefixStringDecoder + { + public MinusDecoder() + { + super("-"); + } + } + + public static class PrefixStringDecoder implements Decoder.Text + { + private final String prefix; + + public PrefixStringDecoder(String prefix) + { + this.prefix = prefix; + } + + @Override + public String decode(String s) + { + return s.substring(prefix.length()) + prefix; + } + + @Override + public boolean willDecode(String s) + { + return s.startsWith(prefix); + } + + @Override + public void init(EndpointConfig config) + { + } + + @Override + public void destroy() + { + } + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableDecodersTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableDecodersTest.java index ff8050c6bbf2..222ffc10c2ee 100644 --- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableDecodersTest.java +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableDecodersTest.java @@ -22,6 +22,7 @@ import java.time.Instant; import java.util.Calendar; import java.util.Date; +import java.util.NoSuchElementException; import java.util.TimeZone; import javax.websocket.DecodeException; import javax.websocket.Decoder; @@ -31,6 +32,7 @@ import org.eclipse.jetty.websocket.javax.client.internal.BasicClientEndpointConfig; import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; import org.eclipse.jetty.websocket.javax.common.decoders.IntegerDecoder; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.util.InvalidWebSocketException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -52,132 +54,145 @@ public static void initConfig() testConfig = new BasicClientEndpointConfig(); } - private AvailableDecoders decoders = new AvailableDecoders(testConfig); + private final AvailableDecoders decoders = new AvailableDecoders(testConfig); - private void assertTextDecoder(Class type, String value, T expectedDecoded) throws IllegalAccessException, InstantiationException, DecodeException + public T getInstanceFor(Class type) { - Decoder.Text decoder = (Decoder.Text)decoders.getInstanceFor(type); + try + { + RegisteredDecoder registeredDecoder = decoders.getFirstRegisteredDecoder(type); + return registeredDecoder.getInstance(); + } + catch (NoSuchElementException e) + { + throw new InvalidWebSocketException("No Decoder found for type " + type); + } + } + + private void assertTextDecoder(Class type, String value, T expectedDecoded) throws DecodeException + { + Decoder.Text decoder = getInstanceFor(type); assertThat("Decoder instance", decoder, notNullValue()); T decoded = decoder.decode(value); assertThat("Decoded", decoded, is(expectedDecoded)); } private void assertBinaryDecoder(Class type, ByteBuffer value, T expectedDecoded) - throws IllegalAccessException, InstantiationException, DecodeException + throws DecodeException { - Decoder.Binary decoder = (Decoder.Binary)decoders.getInstanceFor(type); + Decoder.Binary decoder = getInstanceFor(type); assertThat("Decoder Class", decoder, notNullValue()); T decoded = decoder.decode(value); assertThat("Decoded", decoded, equalTo(expectedDecoded)); } @Test - public void testCoreDecodeBoolean() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeBoolean() throws DecodeException { Boolean expected = Boolean.TRUE; assertTextDecoder(Boolean.class, "true", expected); } @Test - public void testCoreDecodeboolean() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeboolean() throws DecodeException { boolean expected = false; assertTextDecoder(Boolean.TYPE, "false", expected); } @Test - public void testCoreDecodeByte() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeByte() throws DecodeException { Byte expected = (byte)0x21; assertTextDecoder(Byte.class, "33", expected); } @Test - public void testCoreDecodebyte() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodebyte() throws DecodeException { byte expected = 0x21; assertTextDecoder(Byte.TYPE, "33", expected); } @Test - public void testCoreDecodeCharacter() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeCharacter() throws DecodeException { Character expected = '!'; assertTextDecoder(Character.class, "!", expected); } @Test - public void testCoreDecodechar() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodechar() throws DecodeException { char expected = '!'; assertTextDecoder(Character.TYPE, "!", expected); } @Test - public void testCoreDecodeDouble() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeDouble() throws DecodeException { Double expected = 123.45D; assertTextDecoder(Double.class, "123.45", expected); } @Test - public void testCoreDecodedouble() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodedouble() throws DecodeException { double expected = 123.45D; assertTextDecoder(Double.TYPE, "123.45", expected); } @Test - public void testCoreDecodeFloat() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeFloat() throws DecodeException { Float expected = 123.4567F; assertTextDecoder(Float.class, "123.4567", expected); } @Test - public void testCoreDecodefloat() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodefloat() throws DecodeException { float expected = 123.4567F; assertTextDecoder(Float.TYPE, "123.4567", expected); } @Test - public void testCoreDecodeInteger() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeInteger() throws DecodeException { Integer expected = 1234; assertTextDecoder(Integer.class, "1234", expected); } @Test - public void testCoreDecodeint() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeint() throws DecodeException { int expected = 1234; assertTextDecoder(Integer.TYPE, "1234", expected); } @Test - public void testCoreDecodeLong() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeLong() throws DecodeException { Long expected = 123_456_789L; assertTextDecoder(Long.class, "123456789", expected); } @Test - public void testCoreDecodelong() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodelong() throws DecodeException { long expected = 123_456_789L; assertTextDecoder(Long.TYPE, "123456789", expected); } @Test - public void testCoreDecodeString() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeString() throws DecodeException { String expected = "Hello World"; assertTextDecoder(String.class, "Hello World", expected); } @Test - public void testCoreDecodeByteBuffer() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeByteBuffer() throws DecodeException { ByteBuffer val = Hex.asByteBuffer("112233445566778899"); ByteBuffer expected = Hex.asByteBuffer("112233445566778899"); @@ -185,7 +200,7 @@ public void testCoreDecodeByteBuffer() throws IllegalAccessException, Instantiat } @Test - public void testCoreDecodeByteArray() throws IllegalAccessException, InstantiationException, DecodeException + public void testCoreDecodeByteArray() throws DecodeException { ByteBuffer val = Hex.asByteBuffer("112233445566778899"); byte[] expected = Hex.asByteArray("112233445566778899"); @@ -193,7 +208,7 @@ public void testCoreDecodeByteArray() throws IllegalAccessException, Instantiati } @Test - public void testCustomDecoderInteger() throws IllegalAccessException, InstantiationException, DecodeException + public void testCustomDecoderInteger() throws DecodeException { decoders.register(IntegerDecoder.class); @@ -203,7 +218,7 @@ public void testCustomDecoderInteger() throws IllegalAccessException, Instantiat } @Test - public void testCustomDecoderTime() throws IllegalAccessException, InstantiationException, DecodeException + public void testCustomDecoderTime() throws DecodeException { decoders.register(TimeDecoder.class); @@ -222,7 +237,7 @@ public void testCustomDecoderTime() throws IllegalAccessException, Instantiation } @Test - public void testCustomDecoderDate() throws IllegalAccessException, InstantiationException, DecodeException + public void testCustomDecoderDate() throws DecodeException { decoders.register(DateDecoder.class); @@ -241,7 +256,7 @@ public void testCustomDecoderDate() throws IllegalAccessException, Instantiation } @Test - public void testCustomDecoderDateTime() throws IllegalAccessException, InstantiationException, DecodeException + public void testCustomDecoderDateTime() throws DecodeException { decoders.register(DateTimeDecoder.class); @@ -264,11 +279,11 @@ public void testCustomDecoderDateTime() throws IllegalAccessException, Instantia } @Test - public void testCustomDecoderValidDualText() throws IllegalAccessException, InstantiationException, DecodeException + public void testCustomDecoderValidDualText() throws DecodeException { decoders.register(ValidDualDecoder.class); - AvailableDecoders.RegisteredDecoder registered = decoders.getRegisteredDecoderFor(Integer.class); + RegisteredDecoder registered = decoders.getFirstRegisteredDecoder(Integer.class); assertThat("Registered Decoder for Integer", registered.decoder.getName(), is(ValidDualDecoder.class.getName())); String val = "[1,234,567]"; @@ -278,11 +293,11 @@ public void testCustomDecoderValidDualText() throws IllegalAccessException, Inst } @Test - public void testCustomDecoderValidDualBinary() throws IllegalAccessException, InstantiationException, DecodeException + public void testCustomDecoderValidDualBinary() throws DecodeException { decoders.register(ValidDualDecoder.class); - AvailableDecoders.RegisteredDecoder registered = decoders.getRegisteredDecoderFor(Long.class); + RegisteredDecoder registered = decoders.getFirstRegisteredDecoder(Long.class); assertThat("Registered Decoder for Long", registered.decoder.getName(), is(ValidDualDecoder.class.getName())); ByteBuffer val = ByteBuffer.allocate(16); diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/DecoderTextStreamTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/DecoderTextStreamTest.java index 43aaa9db5b58..c16342452262 100644 --- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/DecoderTextStreamTest.java +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/DecoderTextStreamTest.java @@ -27,11 +27,13 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import javax.websocket.ClientEndpointConfig; import javax.websocket.Decoder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextStreamMessageSink; import org.eclipse.jetty.websocket.javax.tests.FunctionMethod; import org.eclipse.jetty.websocket.javax.tests.client.AbstractClientSessionTest; @@ -64,7 +66,6 @@ public void testQuotesDecoderDirect() throws Exception @Test public void testQuotesDecodedReaderMessageSink() throws Exception { - Decoder.TextStream decoder = new QuotesDecoder(); CompletableFuture futureQuotes = new CompletableFuture<>(); MethodHandle functionHandle = FunctionMethod.getFunctionApplyMethodHandle(); MethodHandle quoteHandle = functionHandle.bindTo((Function)(quotes) -> @@ -80,7 +81,8 @@ public void testQuotesDecodedReaderMessageSink() throws Exception return null; }); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink(session.getCoreSession(), decoder, quoteHandle); + List decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders); List callbacks = new ArrayList<>(); FutureCallback finCallback = null; @@ -107,4 +109,21 @@ public void testQuotesDecodedReaderMessageSink() throws Exception assertThat("Quotes.author", quotes.getAuthor(), is("Benjamin Franklin")); assertThat("Quotes.count", quotes.getQuotes().size(), is(3)); } + + public List toRegisteredDecoderList(Class clazz, Class objectType) + { + Class interfaceType; + if (Decoder.Text.class.isAssignableFrom(clazz)) + interfaceType = Decoder.Text.class; + else if (Decoder.Binary.class.isAssignableFrom(clazz)) + interfaceType = Decoder.Binary.class; + else if (Decoder.TextStream.class.isAssignableFrom(clazz)) + interfaceType = Decoder.TextStream.class; + else if (Decoder.BinaryStream.class.isAssignableFrom(clazz)) + interfaceType = Decoder.BinaryStream.class; + else + throw new IllegalStateException(); + + return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build())); + } }