From c29bad23010b870ccb8223458e1d8905ca7d5305 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 31 Mar 2021 18:36:15 +1100 Subject: [PATCH 1/8] Issue #6106 - add tests for WebSocket/CDI integration. Signed-off-by: Lachlan Roberts --- tests/test-cdi/pom.xml | 18 ++ .../websocket/JavaxWebSocketCdiTest.java | 160 ++++++++++++++++++ .../websocket/JettyWebSocketCdiTest.java | 158 +++++++++++++++++ .../jetty/cdi/tests/websocket/LogFactory.java | 34 ++++ 4 files changed, 370 insertions(+) create mode 100644 tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java create mode 100644 tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java create mode 100644 tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java diff --git a/tests/test-cdi/pom.xml b/tests/test-cdi/pom.xml index e973750bab2..505c36f1b8a 100644 --- a/tests/test-cdi/pom.xml +++ b/tests/test-cdi/pom.xml @@ -48,6 +48,24 @@ jetty-slf4j-impl test + + org.eclipse.jetty.websocket + websocket-javax-server + ${project.version} + test + + + org.eclipse.jetty.websocket + websocket-jetty-server + ${project.version} + test + + + org.eclipse.jetty.websocket + websocket-jetty-client + ${project.version} + test + org.eclipse.jetty.toolchain jetty-test-helper diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java new file mode 100644 index 00000000000..07565fa27dc --- /dev/null +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java @@ -0,0 +1,160 @@ +// +// ======================================================================== +// Copyright (c) Webtide LLC 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.cdi.tests.websocket; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import javax.inject.Inject; +import javax.websocket.ClientEndpoint; +import javax.websocket.CloseReason; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; +import javax.websocket.server.ServerEndpoint; + +import org.eclipse.jetty.cdi.CdiDecoratingListener; +import org.eclipse.jetty.cdi.CdiServletContainerInitializer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.BlockingArrayQueue; +import org.eclipse.jetty.websocket.javax.client.JavaxWebSocketClientContainerProvider; +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.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JavaxWebSocketCdiTest +{ + private Server _server; + private WebSocketContainer _client; + private ServerConnector _connector; + + @BeforeEach + public void before() throws Exception + { + _server = new Server(); + _connector = new ServerConnector(_server); + _server.addConnector(_connector); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + + // Enable Weld + CDI + context.setInitParameter(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, CdiDecoratingListener.MODE); + context.addServletContainerInitializer(new CdiServletContainerInitializer()); + context.addServletContainerInitializer(new org.jboss.weld.environment.servlet.EnhancedListener()); + + // Add WebSocket endpoints + JavaxWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> + wsContainer.addEndpoint(CdiEchoSocket.class)); + + // Add to Server + _server.setHandler(context); + + // Start Server + _server.start(); + + _client = JavaxWebSocketClientContainerProvider.getContainer(null); + } + + @AfterEach + public void after() throws Exception + { + JavaxWebSocketClientContainerProvider.stop(_client); + _server.stop(); + } + + @ClientEndpoint + public static class TestClientEndpoint + { + BlockingArrayQueue _textMessages = new BlockingArrayQueue<>(); + CountDownLatch _closeLatch = new CountDownLatch(1); + + @OnMessage + public void onMessage(String message) + { + _textMessages.add(message); + } + + @OnClose + public void onClose() + { + _closeLatch.countDown(); + } + } + + @Test + public void testBasicEcho() throws Exception + { + // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. + TestClientEndpoint clientEndpoint = new TestClientEndpoint(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); + Session session = _client.connectToServer(clientEndpoint, uri); + session.getBasicRemote().sendText("hello world"); + assertThat(clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS), is("hello world")); + session.close(); + assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); + } + + @ServerEndpoint("/echo") + public static class CdiEchoSocket + { + @Inject + public Logger logger; + + private Session session; + + @OnOpen + public void onOpen(Session session) + { + logger.info("onOpen() session:" + session); + this.session = session; + } + + @OnMessage + public void onMessage(String message) throws IOException + { + this.session.getBasicRemote().sendText(message); + } + + @OnError + public void onError(Throwable t) + { + t.printStackTrace(); + } + + @OnClose + public void onClose(CloseReason close) + { + logger.info("onClose() close:" + close); + this.session = null; + } + } +} diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java new file mode 100644 index 00000000000..0c0b9694cb3 --- /dev/null +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java @@ -0,0 +1,158 @@ +// +// ======================================================================== +// Copyright (c) Webtide LLC 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.cdi.tests.websocket; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import javax.inject.Inject; + +import org.eclipse.jetty.cdi.CdiDecoratingListener; +import org.eclipse.jetty.cdi.CdiServletContainerInitializer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.BlockingArrayQueue; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JettyWebSocketCdiTest +{ + private Server _server; + private WebSocketClient _client; + private ServerConnector _connector; + + @BeforeEach + public void before() throws Exception + { + _server = new Server(); + _connector = new ServerConnector(_server); + _server.addConnector(_connector); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + + // Enable Weld + CDI + context.setInitParameter(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, CdiDecoratingListener.MODE); + context.addServletContainerInitializer(new CdiServletContainerInitializer()); + context.addServletContainerInitializer(new org.jboss.weld.environment.servlet.EnhancedListener()); + + // Add WebSocket endpoints + JettyWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> + wsContainer.addMapping("/echo", CdiEchoSocket.class)); + + // Add to Server + _server.setHandler(context); + + // Start Server + _server.start(); + + _client = new WebSocketClient(); + _client.start(); + } + + @AfterEach + public void after() throws Exception + { + _client.stop(); + _server.stop(); + } + + @WebSocket + public static class TestClientEndpoint + { + BlockingArrayQueue _textMessages = new BlockingArrayQueue<>(); + CountDownLatch _closeLatch = new CountDownLatch(1); + + @OnWebSocketMessage + public void onMessage(String message) + { + _textMessages.add(message); + } + + @OnWebSocketClose + public void onClose() + { + _closeLatch.countDown(); + } + } + + @Test + public void testBasicEcho() throws Exception + { + // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. + TestClientEndpoint clientEndpoint = new TestClientEndpoint(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); + Session session = _client.connect(clientEndpoint, uri).get(5, TimeUnit.SECONDS); + session.getRemote().sendString("hello world"); + assertThat(clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS), is("hello world")); + session.close(); + assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); + } + + @WebSocket() + public static class CdiEchoSocket + { + @Inject + public Logger logger; + + private Session session; + + @OnWebSocketConnect + public void onOpen(Session session) + { + logger.info("onOpen() session:" + session); + this.session = session; + } + + @OnWebSocketMessage + public void onMessage(String message) throws IOException + { + this.session.getRemote().sendString(message); + } + + @OnWebSocketError + public void onError(Throwable t) + { + t.printStackTrace(); + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) + { + logger.info("onClose() close: " + statusCode + ":" + reason); + this.session = null; + } + } +} diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java new file mode 100644 index 00000000000..b201259e636 --- /dev/null +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) Webtide LLC 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.cdi.tests.websocket; + +import java.util.logging.Logger; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.InjectionPoint; + +public class LogFactory +{ + @Produces + @Default + public Logger createLogger(InjectionPoint injectionPoint) + { + return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); + } +} From ac5eb54e1e0a3903c2c6f95192bf0116e1468f1b Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 31 Mar 2021 18:44:36 +1100 Subject: [PATCH 2/8] Issue #6106 - fix WebSocket/CDI integration Signed-off-by: Lachlan Roberts --- .../core/server/WebSocketServerComponents.java | 9 ++++++--- .../javax/common/JavaxWebSocketFrameHandler.java | 3 +++ .../common/JettyWebSocketFrameHandler.java | 3 +++ .../tests/websocket/JavaxWebSocketCdiTest.java | 15 +++++---------- .../tests/websocket/JettyWebSocketCdiTest.java | 15 +++++---------- .../jetty/cdi/tests/websocket/LogFactory.java | 15 +++++---------- 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java index c8717bacb16..c5c8bf77a6c 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketServerComponents.java @@ -40,9 +40,9 @@ public class WebSocketServerComponents extends WebSocketComponents public static final String WEBSOCKET_DEFLATER_POOL_ATTRIBUTE = "jetty.websocket.deflater"; public static final String WEBSOCKET_BUFFER_POOL_ATTRIBUTE = "jetty.websocket.bufferPool"; - WebSocketServerComponents(InflaterPool inflaterPool, DeflaterPool deflaterPool, ByteBufferPool bufferPool) + WebSocketServerComponents(InflaterPool inflaterPool, DeflaterPool deflaterPool, ByteBufferPool bufferPool, DecoratedObjectFactory objectFactory) { - super(null, null, bufferPool, inflaterPool, deflaterPool); + super(null, objectFactory, bufferPool, inflaterPool, deflaterPool); } /** @@ -79,7 +79,10 @@ public class WebSocketServerComponents extends WebSocketComponents if (bufferPool == null) bufferPool = server.getBean(ByteBufferPool.class); - WebSocketComponents serverComponents = new WebSocketServerComponents(inflaterPool, deflaterPool, bufferPool); + DecoratedObjectFactory objectFactory = (DecoratedObjectFactory)servletContext.getAttribute(DecoratedObjectFactory.ATTR); + WebSocketComponents serverComponents = new WebSocketServerComponents(inflaterPool, deflaterPool, bufferPool, objectFactory); + if (objectFactory != null) + serverComponents.unmanage(objectFactory); // These components may be managed by the server but not yet started. // In this case we don't want them to be managed by the components as well. 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 21c6e28a533..8df5ed06703 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 @@ -168,6 +168,9 @@ public class JavaxWebSocketFrameHandler implements FrameHandler binaryMetadata = actualBinaryMetadata; } + // Decorate the endpointInstance directly before calling the onOpen method. + coreSession.getWebSocketComponents().getObjectFactory().decorate(endpointInstance); + if (openHandle != null) openHandle.invoke(); diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java index d3235ad0ed7..db9f34bb039 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java @@ -169,6 +169,9 @@ public class JettyWebSocketFrameHandler implements FrameHandler if (binaryHandle != null) binarySink = JettyWebSocketFrameHandlerFactory.createMessageSink(binaryHandle, binarySinkClass, executor, session); + // Decorate the endpointInstance directly before calling the onOpen method. + coreSession.getWebSocketComponents().getObjectFactory().decorate(endpointInstance); + if (openHandle != null) openHandle.invoke(); diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java index 07565fa27dc..08fdfd0dd14 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java @@ -1,16 +1,11 @@ // // ======================================================================== -// Copyright (c) Webtide LLC and others. +// Copyright (c) 1995-2021 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 +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // ======================================================================== diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java index 0c0b9694cb3..794950e85d5 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JettyWebSocketCdiTest.java @@ -1,16 +1,11 @@ // // ======================================================================== -// Copyright (c) Webtide LLC and others. +// Copyright (c) 1995-2021 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 +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // ======================================================================== diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java index b201259e636..1e683c874ec 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/LogFactory.java @@ -1,16 +1,11 @@ // // ======================================================================== -// Copyright (c) Webtide LLC and others. +// Copyright (c) 1995-2021 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 +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // ======================================================================== From a1e522755b7f4c32203f03241a168b2ee5846cec Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 6 Apr 2021 15:37:07 +1000 Subject: [PATCH 3/8] Issue #6106 - Decorate javax.websocket Encoder & Decoders. Signed-off-by: Lachlan Roberts --- ...vaxWebSocketClientFrameHandlerFactory.java | 2 +- .../javax/common/JavaxWebSocketContainer.java | 5 ++ .../JavaxWebSocketFrameHandlerFactory.java | 5 +- .../JavaxWebSocketFrameHandlerMetadata.java | 7 +- .../javax/common/JavaxWebSocketSession.java | 4 +- .../common/decoders/AvailableDecoders.java | 10 ++- .../common/decoders/RegisteredDecoder.java | 12 ++-- .../common/encoders/AvailableEncoders.java | 61 +++--------------- .../common/encoders/RegisteredEncoder.java | 64 +++++++++++++++++++ ...bstractJavaxWebSocketFrameHandlerTest.java | 6 +- .../common/DummyFrameHandlerFactory.java | 2 +- .../messages/AbstractMessageSinkTest.java | 5 +- .../internal/JavaxWebSocketCreator.java | 2 - ...vaxWebSocketServerFrameHandlerFactory.java | 2 +- .../tests/coders/AvailableDecodersTest.java | 4 +- .../tests/coders/AvailableEncodersTest.java | 4 +- .../tests/coders/DecoderTextStreamTest.java | 5 +- ...tJavaxWebSocketServerFrameHandlerTest.java | 6 +- 18 files changed, 127 insertions(+), 79 deletions(-) create mode 100644 jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/RegisteredEncoder.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 e2ccc8bbf9a..7efad63a8b7 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 @@ -48,7 +48,7 @@ public class JavaxWebSocketClientFrameHandlerFactory extends JavaxWebSocketFrame if (endpointClass.getAnnotation(ClientEndpoint.class) == null) return null; - JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig); + JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig, components); return discoverJavaxFrameHandlerMetadata(endpointClass, metadata); } } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketContainer.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketContainer.java index 04da5c5a4c9..225d5eec8bd 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketContainer.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketContainer.java @@ -66,6 +66,11 @@ public abstract class JavaxWebSocketContainer extends ContainerLifeCycle impleme return components.getObjectFactory(); } + public WebSocketComponents getWebSocketComponents() + { + return components; + } + public long getDefaultAsyncSendTimeout() { return defaultCustomizer.getWriteTimeout().toMillis(); 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 66d49e0ac42..57765cdd7e4 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 @@ -38,6 +38,7 @@ import javax.websocket.Session; import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec; import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.core.internal.messages.MessageSink; @@ -102,10 +103,12 @@ public abstract class JavaxWebSocketFrameHandlerFactory protected final JavaxWebSocketContainer container; protected final InvokerUtils.ParamIdentifier paramIdentifier; + protected final WebSocketComponents components; public JavaxWebSocketFrameHandlerFactory(JavaxWebSocketContainer container, InvokerUtils.ParamIdentifier paramIdentifier) { this.container = container; + this.components = container.getWebSocketComponents(); this.paramIdentifier = paramIdentifier == null ? InvokerUtils.PARAM_IDENTITY : paramIdentifier; } @@ -248,7 +251,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory protected JavaxWebSocketFrameHandlerMetadata createEndpointMetadata(EndpointConfig endpointConfig) { - JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig); + JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig, container.getWebSocketComponents()); MethodHandles.Lookup lookup = getServerMethodHandleLookup(); Method openMethod = ReflectUtils.findMethod(Endpoint.class, "onOpen", Session.class, EndpointConfig.class); 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 6671a876dfc..48fece1e46c 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 @@ -18,6 +18,7 @@ import javax.websocket.Encoder; import javax.websocket.EndpointConfig; import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders; @@ -64,10 +65,10 @@ public class JavaxWebSocketFrameHandlerMetadata */ private UriTemplatePathSpec uriTemplatePathSpec; - public JavaxWebSocketFrameHandlerMetadata(EndpointConfig endpointConfig) + public JavaxWebSocketFrameHandlerMetadata(EndpointConfig endpointConfig, WebSocketComponents components) { - this.availableDecoders = new AvailableDecoders(endpointConfig); - this.availableEncoders = new AvailableEncoders(endpointConfig); + this.availableDecoders = new AvailableDecoders(endpointConfig, components); + this.availableEncoders = new AvailableEncoders(endpointConfig, components); } public AvailableDecoders getAvailableDecoders() 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 541d4d9fc20..2f908e5159b 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 @@ -74,8 +74,8 @@ public class JavaxWebSocketSession implements javax.websocket.Session this.coreSession = coreSession; this.frameHandler = frameHandler; this.sessionId = UUID.randomUUID().toString(); - this.availableDecoders = new AvailableDecoders(endpointConfig); - this.availableEncoders = new AvailableEncoders(endpointConfig); + this.availableDecoders = new AvailableDecoders(endpointConfig, coreSession.getWebSocketComponents()); + this.availableEncoders = new AvailableEncoders(endpointConfig, coreSession.getWebSocketComponents()); if (endpointConfig instanceof PathParamProvider) { 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 4f89fd8d19a..e0bbe141404 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 @@ -25,6 +25,7 @@ import java.util.stream.Stream; import javax.websocket.Decoder; import javax.websocket.EndpointConfig; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils; @@ -33,9 +34,12 @@ public class AvailableDecoders implements Iterable { private final List registeredDecoders = new ArrayList<>(); private final EndpointConfig config; + private final WebSocketComponents components; - public AvailableDecoders(EndpointConfig config) + public AvailableDecoders(EndpointConfig config, WebSocketComponents components) { + this.components = Objects.requireNonNull(components); + // Register the Config Based Decoders. this.config = Objects.requireNonNull(config); registerAll(config.getDecoders()); @@ -72,7 +76,7 @@ public class AvailableDecoders implements Iterable private void registerPrimitive(Class decoderClass, Class interfaceType, Class type) { - registeredDecoders.add(new RegisteredDecoder(decoderClass, interfaceType, type, config, true)); + registeredDecoders.add(new RegisteredDecoder(decoderClass, interfaceType, type, config, components, true)); } private void register(Class decoder) @@ -151,7 +155,7 @@ public class AvailableDecoders implements Iterable return; } - registeredDecoders.add(new RegisteredDecoder(decoder, interfaceClass, objectType, config)); + registeredDecoders.add(new RegisteredDecoder(decoder, interfaceClass, objectType, config, components)); } public RegisteredDecoder getFirstRegisteredDecoder(Class type) 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 index be4d69ccbed..3608d7796b4 100644 --- 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 @@ -17,10 +17,13 @@ import java.lang.reflect.InvocationTargetException; import javax.websocket.Decoder; import javax.websocket.EndpointConfig; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.javax.common.InitException; public class RegisteredDecoder { + private final WebSocketComponents components; + // The user supplied Decoder class public final Class decoder; // The javax.websocket.Decoder.* type (eg: Decoder.Binary, Decoder.BinaryStream, Decoder.Text, Decoder.TextStream) @@ -31,18 +34,19 @@ public class RegisteredDecoder private Decoder instance; - public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, EndpointConfig endpointConfig) + public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, EndpointConfig endpointConfig, WebSocketComponents components) { - this(decoder, interfaceType, objectType, endpointConfig, false); + this(decoder, interfaceType, objectType, endpointConfig, components, false); } - public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, EndpointConfig endpointConfig, boolean primitive) + public RegisteredDecoder(Class decoder, Class interfaceType, Class objectType, EndpointConfig endpointConfig, WebSocketComponents components, boolean primitive) { this.decoder = decoder; this.interfaceType = interfaceType; this.objectType = objectType; this.primitive = primitive; this.config = endpointConfig; + this.components = components; } public boolean implementsInterface(Class type) @@ -61,7 +65,7 @@ public class RegisteredDecoder { try { - instance = decoder.getConstructor().newInstance(); + instance = components.getObjectFactory().createInstance(decoder); instance.init(config); return (T)instance; } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/AvailableEncoders.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/AvailableEncoders.java index 3d351a90566..45ba8718fa2 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/AvailableEncoders.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/AvailableEncoders.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import javax.websocket.Encoder; import javax.websocket.EndpointConfig; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils; @@ -31,62 +32,16 @@ import org.eclipse.jetty.websocket.javax.common.InitException; public class AvailableEncoders implements Predicate> { - public static class RegisteredEncoder - { - public final Class encoder; - public final Class interfaceType; - public final Class objectType; - public final boolean primitive; - public Encoder instance; - - public RegisteredEncoder(Class encoder, Class interfaceType, Class objectType) - { - this(encoder, interfaceType, objectType, false); - } - - public RegisteredEncoder(Class encoder, Class interfaceType, Class objectType, boolean primitive) - { - this.encoder = encoder; - 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(AvailableEncoders.RegisteredEncoder.class.getSimpleName()); - str.append('[').append(encoder.getName()); - str.append(',').append(interfaceType.getName()); - str.append(',').append(objectType.getName()); - if (primitive) - { - str.append(",PRIMITIVE"); - } - str.append(']'); - return str.toString(); - } - } private final EndpointConfig config; - private LinkedList registeredEncoders; + private final WebSocketComponents components; + private final LinkedList registeredEncoders; - public AvailableEncoders(EndpointConfig config) + public AvailableEncoders(EndpointConfig config, WebSocketComponents components) { - Objects.requireNonNull(config); - this.config = config; - registeredEncoders = new LinkedList<>(); + this.config = Objects.requireNonNull(config); + this.components = Objects.requireNonNull(components); + this.registeredEncoders = new LinkedList<>(); // TEXT based [via Class reference] registerPrimitive(BooleanEncoder.class, Encoder.Text.class, Boolean.class); @@ -267,7 +222,7 @@ public class AvailableEncoders implements Predicate> return registeredEncoder.instance; } - registeredEncoder.instance = registeredEncoder.encoder.getConstructor().newInstance(); + registeredEncoder.instance = components.getObjectFactory().createInstance(registeredEncoder.encoder); registeredEncoder.instance.init(this.config); return registeredEncoder.instance; } diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/RegisteredEncoder.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/RegisteredEncoder.java new file mode 100644 index 00000000000..e15ce8c4a44 --- /dev/null +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/encoders/RegisteredEncoder.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.encoders; + +import javax.websocket.Encoder; + +public class RegisteredEncoder +{ + public final Class encoder; + public final Class interfaceType; + public final Class objectType; + public final boolean primitive; + public Encoder instance; + + public RegisteredEncoder(Class encoder, Class interfaceType, Class objectType) + { + this(encoder, interfaceType, objectType, false); + } + + public RegisteredEncoder(Class encoder, Class interfaceType, Class objectType, boolean primitive) + { + this.encoder = encoder; + 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(RegisteredEncoder.class.getSimpleName()); + str.append('[').append(encoder.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/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java index 31c6aef0e17..64e1703f934 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java @@ -19,6 +19,7 @@ import javax.websocket.ClientEndpointConfig; import javax.websocket.EndpointConfig; import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders; import org.junit.jupiter.api.AfterAll; @@ -46,12 +47,13 @@ public abstract class AbstractJavaxWebSocketFrameHandlerTest protected Map uriParams; protected EndpointConfig endpointConfig; protected CoreSession coreSession = new CoreSession.Empty(); + private WebSocketComponents components = new WebSocketComponents(); public AbstractJavaxWebSocketFrameHandlerTest() { endpointConfig = ClientEndpointConfig.Builder.create().build(); - encoders = new AvailableEncoders(endpointConfig); - decoders = new AvailableDecoders(endpointConfig); + encoders = new AvailableEncoders(endpointConfig, components); + decoders = new AvailableDecoders(endpointConfig, components); uriParams = new HashMap<>(); } diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java index b8757660033..7175e37fe58 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java @@ -45,7 +45,7 @@ public class DummyFrameHandlerFactory extends JavaxWebSocketFrameHandlerFactory return null; } - JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig); + JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig, components); return discoverJavaxFrameHandlerMetadata(endpointClass, metadata); } } 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 839440f2429..83682c9a413 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,12 +20,15 @@ import java.util.function.Consumer; import javax.websocket.ClientEndpointConfig; import javax.websocket.Decoder; +import org.eclipse.jetty.websocket.core.WebSocketComponents; 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 { + private final WebSocketComponents _components = new WebSocketComponents(); + public List toRegisteredDecoderList(Class clazz, Class objectType) { Class interfaceType; @@ -40,7 +43,7 @@ public abstract class AbstractMessageSinkTest extends AbstractSessionTest else throw new IllegalStateException(); - return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build())); + return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build(), _components)); } public MethodHandle getAcceptHandle(Consumer copy, Class type) diff --git a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketCreator.java b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketCreator.java index 83249769551..347238eb702 100644 --- a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketCreator.java +++ b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketCreator.java @@ -159,8 +159,6 @@ public class JavaxWebSocketCreator implements WebSocketCreator // [JSR] Step 6: create endpoint class Class endpointClass = config.getEndpointClass(); Object endpoint = config.getConfigurator().getEndpointInstance(endpointClass); - // Do not decorate here (let the Connection and Session start first) - // This will allow CDI to see Session for injection into Endpoint classes. return new ConfiguredEndpoint(endpoint, config); } catch (InstantiationException e) 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 1a5ff15b9be..17e2d651d6e 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 @@ -43,7 +43,7 @@ public class JavaxWebSocketServerFrameHandlerFactory extends JavaxWebSocketClien return super.getMetadata(endpointClass, endpointConfig); UriTemplatePathSpec templatePathSpec = new UriTemplatePathSpec(anno.value()); - JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig); + JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig, components); metadata.setUriTemplatePathSpec(templatePathSpec); return discoverJavaxFrameHandlerMetadata(endpointClass, metadata); } 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 6da6ffa0bc2..461fd1c0792 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 @@ -26,6 +26,7 @@ import javax.websocket.Decoder; import javax.websocket.EndpointConfig; import org.eclipse.jetty.toolchain.test.Hex; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; import org.eclipse.jetty.websocket.javax.common.decoders.IntegerDecoder; @@ -42,6 +43,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class AvailableDecodersTest { private AvailableDecoders availableDecoders; + private WebSocketComponents components = new WebSocketComponents(); @SafeVarargs public final void init(Class... decoder) @@ -49,7 +51,7 @@ public class AvailableDecodersTest EndpointConfig testConfig = ClientEndpointConfig.Builder.create() .decoders(Arrays.asList(decoder)) .build(); - this.availableDecoders = new AvailableDecoders(testConfig); + this.availableDecoders = new AvailableDecoders(testConfig, components); } public T getInstanceFor(Class type) diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableEncodersTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableEncodersTest.java index 1905dd3ede3..57692790fa9 100644 --- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableEncodersTest.java +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/coders/AvailableEncodersTest.java @@ -25,6 +25,7 @@ import javax.websocket.Encoder; import javax.websocket.EndpointConfig; import org.eclipse.jetty.toolchain.test.Hex; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.javax.client.internal.BasicClientEndpointConfig; import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders; @@ -41,6 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class AvailableEncodersTest { private static EndpointConfig testConfig; + private final WebSocketComponents components = new WebSocketComponents(); @BeforeAll public static void initConfig() @@ -48,7 +50,7 @@ public class AvailableEncodersTest testConfig = new BasicClientEndpointConfig(); } - private AvailableEncoders encoders = new AvailableEncoders(testConfig); + private final AvailableEncoders encoders = new AvailableEncoders(testConfig, components); public void assertTextEncoder(Class type, T value, String expectedEncoded) throws IllegalAccessException, InstantiationException, EncodeException { 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 ae8850901d6..c5401959478 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 @@ -28,6 +28,7 @@ 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.core.WebSocketComponents; 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; @@ -43,6 +44,8 @@ import static org.hamcrest.Matchers.notNullValue; */ public class DecoderTextStreamTest extends AbstractClientSessionTest { + private final WebSocketComponents _components = new WebSocketComponents(); + @Test public void testQuotesDecoderDirect() throws Exception { @@ -119,6 +122,6 @@ public class DecoderTextStreamTest extends AbstractClientSessionTest else throw new IllegalStateException(); - return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build())); + return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build(), _components)); } } diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java index d52e7c7b0c2..d51177fd52f 100644 --- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java @@ -19,6 +19,7 @@ import javax.websocket.EndpointConfig; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.websocket.core.WebSocketComponents; 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.encoders.AvailableEncoders; @@ -54,12 +55,13 @@ public abstract class AbstractJavaxWebSocketServerFrameHandlerTest protected AvailableDecoders decoders; protected Map uriParams; protected EndpointConfig endpointConfig; + private WebSocketComponents components = new WebSocketComponents(); public AbstractJavaxWebSocketServerFrameHandlerTest() { endpointConfig = new BasicClientEndpointConfig(); - encoders = new AvailableEncoders(endpointConfig); - decoders = new AvailableDecoders(endpointConfig); + encoders = new AvailableEncoders(endpointConfig, components); + decoders = new AvailableDecoders(endpointConfig, components); uriParams = new HashMap<>(); } } From f858aa653c947c81959f17dec8c9ec82789ec611 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 6 Apr 2021 22:39:15 +1000 Subject: [PATCH 4/8] Issue #6106 - always decorate javax.websocket Configurators Signed-off-by: Lachlan Roberts --- .../core/client/CoreClientUpgradeRequest.java | 20 +- .../AnnotatedClientEndpointConfig.java | 5 +- .../JavaxWebSocketClientContainer.java | 14 +- .../AnnotatedServerEndpointConfig.java | 8 +- .../JavaxWebSocketServerContainer.java | 3 + .../websocket/JavaxWebSocketCdiTest.java | 200 +++++++++++++++--- 6 files changed, 209 insertions(+), 41 deletions(-) diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index 5ba4d7355b6..f0b139ac0da 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -40,6 +40,7 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.websocket.core.Behavior; @@ -291,7 +292,12 @@ public abstract class CoreClientUpgradeRequest extends HttpRequest implements Re headers(headers -> headers.add(HttpHeader.SEC_WEBSOCKET_EXTENSIONS, extensionString)); // Notify the listener which may change the headers directly. - notifyUpgradeListeners((listener) -> listener.onHandshakeRequest(this)); + Exception listenerError = notifyUpgradeListeners((listener) -> listener.onHandshakeRequest(this)); + if (listenerError != null) + { + abort(listenerError); + return; + } // Check if extensions were set in the headers from the upgrade listener. String extsAfterListener = String.join(",", getHeaders().getCSV(HttpHeader.SEC_WEBSOCKET_EXTENSIONS, true)); @@ -306,8 +312,9 @@ public abstract class CoreClientUpgradeRequest extends HttpRequest implements Re } } - private void notifyUpgradeListeners(Consumer action) + private Exception notifyUpgradeListeners(Consumer action) { + MultiException multiException = null; for (UpgradeListener listener : upgradeListeners) { try @@ -317,8 +324,13 @@ public abstract class CoreClientUpgradeRequest extends HttpRequest implements Re catch (Throwable t) { LOG.info("Exception while invoking listener {}", listener, t); + if (multiException == null) + multiException = new MultiException(); + multiException.add(t); } } + + return multiException; } public void upgrade(HttpResponse response, EndPoint endPoint) @@ -437,7 +449,9 @@ public abstract class CoreClientUpgradeRequest extends HttpRequest implements Re WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); - notifyUpgradeListeners((listener) -> listener.onHandshakeResponse(this, response)); + Exception listenerError = notifyUpgradeListeners((listener) -> listener.onHandshakeResponse(this, response)); + if (listenerError != null) + throw new WebSocketException("onHandshakeResponse error", listenerError); // Now swap out the connection try diff --git a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/AnnotatedClientEndpointConfig.java b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/AnnotatedClientEndpointConfig.java index df3c677cd86..dca6437e73d 100644 --- a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/AnnotatedClientEndpointConfig.java +++ b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/AnnotatedClientEndpointConfig.java @@ -18,17 +18,18 @@ import java.util.List; import javax.websocket.ClientEndpoint; import javax.websocket.ClientEndpointConfig; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.javax.common.ClientEndpointConfigWrapper; public class AnnotatedClientEndpointConfig extends ClientEndpointConfigWrapper { - public AnnotatedClientEndpointConfig(ClientEndpoint anno) + public AnnotatedClientEndpointConfig(ClientEndpoint anno, WebSocketComponents components) { Configurator configurator; try { - configurator = anno.configurator().getDeclaredConstructor().newInstance(); + configurator = components.getObjectFactory().createInstance(anno.configurator()); } catch (Exception e) { diff --git a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientContainer.java b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientContainer.java index 1776aae14f8..52cda26afd2 100644 --- a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientContainer.java +++ b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientContainer.java @@ -229,9 +229,16 @@ public class JavaxWebSocketClientContainer extends JavaxWebSocketContainer imple @Override public Session connectToServer(final Endpoint endpoint, final ClientEndpointConfig providedConfig, final URI path) throws DeploymentException, IOException { - ClientEndpointConfig config = providedConfig; - if (config == null) + ClientEndpointConfig config; + if (providedConfig == null) + { config = new BasicClientEndpointConfig(); + } + else + { + config = providedConfig; + components.getObjectFactory().decorate(providedConfig.getConfigurator()); + } ConfiguredEndpoint instance = new ConfiguredEndpoint(endpoint, config); return connect(instance, path); @@ -240,6 +247,7 @@ public class JavaxWebSocketClientContainer extends JavaxWebSocketContainer imple @Override public Session connectToServer(Object endpoint, URI path) throws DeploymentException, IOException { + // The Configurator will be decorated when it is created in the getAnnotatedConfig method. ClientEndpointConfig config = getAnnotatedConfig(endpoint); ConfiguredEndpoint instance = new ConfiguredEndpoint(endpoint, config); return connect(instance, path); @@ -275,7 +283,7 @@ public class JavaxWebSocketClientContainer extends JavaxWebSocketContainer imple if (anno == null) throw new DeploymentException("Could not get ClientEndpoint annotation for " + endpoint.getClass().getName()); - return new AnnotatedClientEndpointConfig(anno); + return new AnnotatedClientEndpointConfig(anno, components); } @Override diff --git a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/AnnotatedServerEndpointConfig.java b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/AnnotatedServerEndpointConfig.java index 4d960c4a25b..4424722f7d7 100644 --- a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/AnnotatedServerEndpointConfig.java +++ b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/AnnotatedServerEndpointConfig.java @@ -70,9 +70,7 @@ public class AnnotatedServerEndpointConfig extends ServerEndpointConfigWrapper else path = anno.value(); - // Make sure all Configurators obtained are decorated. - ServerEndpointConfig.Configurator rawConfigurator = getConfigurator(baseServerConfig, anno); - ServerEndpointConfig.Configurator configurator = containerScope.getObjectFactory().decorate(rawConfigurator); + ServerEndpointConfig.Configurator configurator = getConfigurator(baseServerConfig, anno, containerScope); // Build a ServerEndpointConfig with the Javax API builder to wrap. ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder.create(endpointClass, path) @@ -90,7 +88,7 @@ public class AnnotatedServerEndpointConfig extends ServerEndpointConfigWrapper init(endpointConfig); } - private static Configurator getConfigurator(ServerEndpointConfig baseServerConfig, ServerEndpoint anno) throws DeploymentException + private static Configurator getConfigurator(ServerEndpointConfig baseServerConfig, ServerEndpoint anno, JavaxWebSocketContainer containerScope) throws DeploymentException { Configurator ret = null; @@ -115,7 +113,7 @@ public class AnnotatedServerEndpointConfig extends ServerEndpointConfigWrapper // Instantiate the provided configurator try { - return anno.configurator().getConstructor().newInstance(); + return containerScope.getObjectFactory().createInstance(anno.configurator()); } catch (Exception e) { diff --git a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerContainer.java b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerContainer.java index f7f67507e56..b7b2e029bed 100644 --- a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerContainer.java +++ b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerContainer.java @@ -232,6 +232,9 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer if (isStarted() || isStarting()) { + // Decorate the provided Configurator. + components.getObjectFactory().decorate(providedConfig.getConfigurator()); + // If we have annotations merge the annotated ServerEndpointConfig with the provided one. Class endpointClass = providedConfig.getEndpointClass(); ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class); diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java index 08fdfd0dd14..0dfc47d1293 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java @@ -15,19 +15,28 @@ package org.eclipse.jetty.cdi.tests.websocket; import java.io.IOException; import java.net.URI; +import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import javax.inject.Inject; import javax.websocket.ClientEndpoint; +import javax.websocket.ClientEndpointConfig; import javax.websocket.CloseReason; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.HandshakeResponse; +import javax.websocket.MessageHandler; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.WebSocketContainer; +import javax.websocket.server.HandshakeRequest; import javax.websocket.server.ServerEndpoint; +import javax.websocket.server.ServerEndpointConfig; import org.eclipse.jetty.cdi.CdiDecoratingListener; import org.eclipse.jetty.cdi.CdiServletContainerInitializer; @@ -35,8 +44,13 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.BlockingArrayQueue; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.websocket.core.WebSocketComponents; +import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents; import org.eclipse.jetty.websocket.javax.client.JavaxWebSocketClientContainerProvider; +import org.eclipse.jetty.websocket.javax.client.internal.JavaxWebSocketClientContainer; import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer; +import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -50,15 +64,16 @@ public class JavaxWebSocketCdiTest private Server _server; private WebSocketContainer _client; private ServerConnector _connector; + private ServletContextHandler context; @BeforeEach - public void before() throws Exception + public void before() { _server = new Server(); _connector = new ServerConnector(_server); _server.addConnector(_connector); - ServletContextHandler context = new ServletContextHandler(); + context = new ServletContextHandler(); context.setContextPath("/"); // Enable Weld + CDI @@ -66,17 +81,22 @@ public class JavaxWebSocketCdiTest context.addServletContainerInitializer(new CdiServletContainerInitializer()); context.addServletContainerInitializer(new org.jboss.weld.environment.servlet.EnhancedListener()); - // Add WebSocket endpoints - JavaxWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> - wsContainer.addEndpoint(CdiEchoSocket.class)); - // Add to Server _server.setHandler(context); + } + + public void start(Configurator configurator) throws Exception + { + // Add WebSocket endpoints + JavaxWebSocketServletContainerInitializer.configure(context, configurator); // Start Server _server.start(); - _client = JavaxWebSocketClientContainerProvider.getContainer(null); + // Configure the Client with the same DecoratedObjectFactory from the server. + WebSocketComponents components = WebSocketServerComponents.getWebSocketComponents(context.getServletContext()); + _client = new JavaxWebSocketClientContainer(components); + LifeCycle.start(_client); } @AfterEach @@ -86,18 +106,94 @@ public class JavaxWebSocketCdiTest _server.stop(); } - @ClientEndpoint - public static class TestClientEndpoint + @Test + public void testAnnotatedEndpoint() throws Exception { + start((servletContext, wsContainer) -> wsContainer.addEndpoint(AnnotatedCdiEchoSocket.class)); + + // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. + AnnotatedCdiClientSocket clientEndpoint = new AnnotatedCdiClientSocket(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); + Session session = _client.connectToServer(clientEndpoint, uri); + session.getBasicRemote().sendText("hello world"); + assertThat(clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS), is("hello world")); + session.close(); + assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testConfiguredEndpoint() throws Exception + { + ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(ConfiguredCdiEchoSocket.class, "/echo") + .configurator(new ServerConfigurator()) + .build(); + start((servletContext, wsContainer) -> wsContainer.addEndpoint(serverEndpointConfig)); + + // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. + ConfiguredCdiClientSocket clientEndpoint = new ConfiguredCdiClientSocket(); + ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create() + .configurator(new ClientConfigurator()) + .build(); + + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); + Session session = _client.connectToServer(clientEndpoint, clientEndpointConfig, uri); + session.getBasicRemote().sendText("hello world"); + assertThat(clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS), is("hello world")); + session.close(); + assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); + } + + public static class ClientConfigurator extends ClientEndpointConfig.Configurator + { + @Inject + public Logger logger; + + @Override + public void beforeRequest(Map> headers) + { + logger.info("beforeRequest"); + } + } + + public static class ServerConfigurator extends ServerEndpointConfig.Configurator + { + @Inject + public Logger logger; + + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) + { + logger.info("modifyHandshake"); + } + } + + @ClientEndpoint(configurator = ClientConfigurator.class) + public static class AnnotatedCdiClientSocket + { + @Inject + public Logger logger; + BlockingArrayQueue _textMessages = new BlockingArrayQueue<>(); CountDownLatch _closeLatch = new CountDownLatch(1); + @OnOpen + public void onOpen(Session session) + { + logger.info("onOpen: " + session); + } + @OnMessage public void onMessage(String message) { _textMessages.add(message); } + @OnError + public void onError(Throwable t) + { + t.printStackTrace(); + } + @OnClose public void onClose() { @@ -105,21 +201,8 @@ public class JavaxWebSocketCdiTest } } - @Test - public void testBasicEcho() throws Exception - { - // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. - TestClientEndpoint clientEndpoint = new TestClientEndpoint(); - URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); - Session session = _client.connectToServer(clientEndpoint, uri); - session.getBasicRemote().sendText("hello world"); - assertThat(clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS), is("hello world")); - session.close(); - assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); - } - - @ServerEndpoint("/echo") - public static class CdiEchoSocket + @ServerEndpoint(value = "/echo", configurator = ServerConfigurator.class) + public static class AnnotatedCdiEchoSocket { @Inject public Logger logger; @@ -144,12 +227,73 @@ public class JavaxWebSocketCdiTest { t.printStackTrace(); } + } - @OnClose - public void onClose(CloseReason close) + public static class ConfiguredCdiClientSocket extends Endpoint implements MessageHandler.Whole + { + @Inject + public Logger logger; + + BlockingArrayQueue _textMessages = new BlockingArrayQueue<>(); + CountDownLatch _closeLatch = new CountDownLatch(1); + + @Override + public void onMessage(String message) { - logger.info("onClose() close:" + close); - this.session = null; + _textMessages.add(message); + } + + @Override + public void onOpen(Session session, EndpointConfig config) + { + logger.info("onOpen: " + session); + session.addMessageHandler(this); + } + + @Override + public void onError(Session session, Throwable thr) + { + thr.printStackTrace(); + } + + @Override + public void onClose(Session session, CloseReason closeReason) + { + _closeLatch.countDown(); + } + } + + public static class ConfiguredCdiEchoSocket extends Endpoint implements MessageHandler.Whole + { + @Inject + public Logger logger; + private Session session; + + @Override + public void onMessage(String message) + { + try + { + session.getBasicRemote().sendText(message); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + @Override + public void onOpen(Session session, EndpointConfig config) + { + logger.info("onOpen() session:" + session); + this.session = session; + session.addMessageHandler(this); + } + + @Override + public void onError(Session session, Throwable thr) + { + thr.printStackTrace(); } } } From bda4c21bcb65877df36cac7489206f29655f96e0 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 6 Apr 2021 22:58:23 +1000 Subject: [PATCH 5/8] Issue #6106 - add testing for CDI with javax.websocket Encoder & Decoder Signed-off-by: Lachlan Roberts --- .../websocket/JavaxWebSocketCdiTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java index 0dfc47d1293..ba532296d9f 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java @@ -24,6 +24,8 @@ import javax.inject.Inject; import javax.websocket.ClientEndpoint; import javax.websocket.ClientEndpointConfig; import javax.websocket.CloseReason; +import javax.websocket.Decoder; +import javax.websocket.Encoder; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.HandshakeResponse; @@ -143,6 +145,21 @@ public class JavaxWebSocketCdiTest assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); } + @Test + public void testEncoderDecoder() throws Exception + { + start((servletContext, wsContainer) -> wsContainer.addEndpoint(AnnotatedCdiEchoSocket.class)); + + // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. + AnnotatedEncoderDecoderClientSocket clientEndpoint = new AnnotatedEncoderDecoderClientSocket(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); + Session session = _client.connectToServer(clientEndpoint, uri); + session.getBasicRemote().sendObject("hello world"); + assertThat(clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS), is("decoded(encoded(hello world))")); + session.close(); + assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); + } + public static class ClientConfigurator extends ClientEndpointConfig.Configurator { @Inject @@ -296,4 +313,61 @@ public class JavaxWebSocketCdiTest thr.printStackTrace(); } } + + public static class CustomEncoder implements Encoder.Text + { + @Inject + public Logger logger; + + @Override + public String encode(String s) + { + return "encoded(" + s + ")"; + } + + @Override + public void init(EndpointConfig config) + { + logger.info("init: " + config); + } + + @Override + public void destroy() + { + } + } + + public static class CustomDecoder implements Decoder.Text + { + @Inject + public Logger logger; + + @Override + public String decode(String s) + { + return "decoded(" + s + ")"; + } + + @Override + public void init(EndpointConfig config) + { + logger.info("init: " + config); + } + + @Override + public void destroy() + { + } + + @Override + public boolean willDecode(String s) + { + return true; + } + } + + @ClientEndpoint(encoders = {CustomEncoder.class}, decoders = {CustomDecoder.class}) + public static class AnnotatedEncoderDecoderClientSocket extends AnnotatedCdiClientSocket + { + } } From 02ef27246c500bd50e1f225c400f520454db68a6 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 6 Apr 2021 23:18:41 +1000 Subject: [PATCH 6/8] Fix broken websocket tests. Signed-off-by: Lachlan Roberts --- .../javax/common/JavaxWebSocketSession.java | 4 +-- ...bstractJavaxWebSocketFrameHandlerTest.java | 16 ++++++--- .../JettyWebSocketFrameHandlerTest.java | 35 ++++++++++++------- 3 files changed, 36 insertions(+), 19 deletions(-) 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 2f908e5159b..81757f5b685 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 @@ -74,8 +74,8 @@ public class JavaxWebSocketSession implements javax.websocket.Session this.coreSession = coreSession; this.frameHandler = frameHandler; this.sessionId = UUID.randomUUID().toString(); - this.availableDecoders = new AvailableDecoders(endpointConfig, coreSession.getWebSocketComponents()); - this.availableEncoders = new AvailableEncoders(endpointConfig, coreSession.getWebSocketComponents()); + this.availableDecoders = new AvailableDecoders(endpointConfig, container.getWebSocketComponents()); + this.availableEncoders = new AvailableEncoders(endpointConfig, container.getWebSocketComponents()); if (endpointConfig instanceof PathParamProvider) { diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java index 64e1703f934..b9b2de4c608 100644 --- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java +++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractJavaxWebSocketFrameHandlerTest.java @@ -46,14 +46,22 @@ public abstract class AbstractJavaxWebSocketFrameHandlerTest protected AvailableDecoders decoders; protected Map uriParams; protected EndpointConfig endpointConfig; - protected CoreSession coreSession = new CoreSession.Empty(); - private WebSocketComponents components = new WebSocketComponents(); + protected CoreSession coreSession = new CoreSession.Empty() + { + private final WebSocketComponents components = new WebSocketComponents(); + + @Override + public WebSocketComponents getWebSocketComponents() + { + return components; + } + }; public AbstractJavaxWebSocketFrameHandlerTest() { endpointConfig = ClientEndpointConfig.Builder.create().build(); - encoders = new AvailableEncoders(endpointConfig, components); - decoders = new AvailableDecoders(endpointConfig, components); + encoders = new AvailableEncoders(endpointConfig, coreSession.getWebSocketComponents()); + decoders = new AvailableDecoders(endpointConfig, coreSession.getWebSocketComponents()); uriParams = new HashMap<>(); } diff --git a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java index 31f041e7f3d..5cf8a32268c 100644 --- a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java +++ b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.websocket.core.CloseStatus; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -39,6 +40,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertTimeout; +import static org.junit.jupiter.api.Assertions.assertTrue; public class JettyWebSocketFrameHandlerTest { @@ -57,20 +59,27 @@ public class JettyWebSocketFrameHandlerTest container.stop(); } - private JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container); - private CoreSession coreSession = new CoreSession.Empty() + private final JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container); + private final CoreSession coreSession = new CoreSession.Empty() { + private final WebSocketComponents components = new WebSocketComponents(); + @Override public Behavior getBehavior() { return Behavior.CLIENT; } + + @Override + public WebSocketComponents getWebSocketComponents() + { + return components; + } }; private JettyWebSocketFrameHandler newLocalFrameHandler(Object wsEndpoint) { - JettyWebSocketFrameHandler localEndpoint = endpointFactory.newJettyFrameHandler(wsEndpoint); - return localEndpoint; + return endpointFactory.newJettyFrameHandler(wsEndpoint); } public static class ConnectionOnly implements WebSocketConnectionListener @@ -97,7 +106,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testConnectionListener() throws Exception + public void testConnectionListener() { ConnectionOnly socket = new ConnectionOnly(); JettyWebSocketFrameHandler localEndpoint = newLocalFrameHandler(socket); @@ -138,7 +147,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testAnnotatedStreamedTextSingle() throws Exception + public void testAnnotatedStreamedTextSingle() { assertTimeout(Duration.ofMillis(1000), () -> { @@ -160,7 +169,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testAnnotatedStreamedTextMultipleParts() throws Exception + public void testAnnotatedStreamedTextMultipleParts() { assertTimeout(Duration.ofMillis(1000), () -> { @@ -177,7 +186,7 @@ public class JettyWebSocketFrameHandlerTest localEndpoint.onFrame(CloseStatus.toFrame(StatusCode.NORMAL, "Normal"), Callback.NOOP); // Await completion (of threads) - socket.streamLatch.await(2, TimeUnit.SECONDS); + assertTrue(socket.streamLatch.await(2, TimeUnit.SECONDS)); // Validate Events socket.events.assertEvents("onTextStream\\(Hello World\\)"); @@ -185,7 +194,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testListenerPartialSocket() throws Exception + public void testListenerPartialSocket() { // Setup EndPoints.ListenerPartialSocket socket = new EndPoints.ListenerPartialSocket(); @@ -216,7 +225,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testListenerBasicSocket() throws Exception + public void testListenerBasicSocket() { // Setup EndPoints.ListenerBasicSocket socket = new EndPoints.ListenerBasicSocket(); @@ -242,7 +251,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testListenerBasicSocketError() throws Exception + public void testListenerBasicSocketError() { // Setup EndPoints.ListenerBasicSocket socket = new EndPoints.ListenerBasicSocket(); @@ -262,7 +271,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testListenerFrameSocket() throws Exception + public void testListenerFrameSocket() { // Setup EndPoints.ListenerFrameSocket socket = new EndPoints.ListenerFrameSocket(); @@ -292,7 +301,7 @@ public class JettyWebSocketFrameHandlerTest } @Test - public void testListenerPingPongSocket() throws Exception + public void testListenerPingPongSocket() { // Setup EndPoints.ListenerPingPongSocket socket = new EndPoints.ListenerPingPongSocket(); From 0d84f35b0da33f1980ac34bcc6f5aa50d50f4a8a Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 7 Apr 2021 16:43:10 +1000 Subject: [PATCH 7/8] Issue #6106 - Decorate javax.websocket components during the websocket upgrade. Decoration should not be delayed until onOpen it should be during the call to the WebSocketUpgradeFilter or WebSocketServlet. Signed-off-by: Lachlan Roberts --- .../common/JavaxWebSocketFrameHandler.java | 3 -- .../JavaxWebSocketFrameHandlerFactory.java | 3 ++ .../websocket/client/WebSocketClient.java | 2 +- .../common/JettyWebSocketFrameHandler.java | 3 -- .../JettyWebSocketFrameHandlerFactory.java | 15 ++++++-- .../JettyWebSocketFrameHandlerTest.java | 5 ++- .../common/LocalEndpointMetadataTest.java | 4 ++- .../server/JettyWebSocketServerContainer.java | 2 +- .../JettyServerFrameHandlerFactory.java | 6 ++-- .../websocket/JavaxWebSocketCdiTest.java | 36 +++++++++++++++++-- 10 files changed, 59 insertions(+), 20 deletions(-) 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 8df5ed06703..21c6e28a533 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 @@ -168,9 +168,6 @@ public class JavaxWebSocketFrameHandler implements FrameHandler binaryMetadata = actualBinaryMetadata; } - // Decorate the endpointInstance directly before calling the onOpen method. - coreSession.getWebSocketComponents().getObjectFactory().decorate(endpointInstance); - if (openHandle != null) openHandle.invoke(); 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 57765cdd7e4..36d40b30f64 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 @@ -168,6 +168,9 @@ public abstract class JavaxWebSocketFrameHandlerFactory errorHandle = InvokerUtils.bindTo(errorHandle, endpoint); pongHandle = InvokerUtils.bindTo(pongHandle, endpoint); + // Decorate the endpointInstance while we are still upgrading for access to things like HttpSession. + components.getObjectFactory().decorate(endpoint); + return new JavaxWebSocketFrameHandler( container, upgradeRequest, diff --git a/jetty-websocket/websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index bc7593dba70..5e184a3f488 100644 --- a/jetty-websocket/websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/websocket-jetty-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -89,7 +89,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli if (httpClient == null) coreClient.getHttpClient().setName("Jetty-WebSocketClient@" + hashCode()); - frameHandlerFactory = new JettyWebSocketFrameHandlerFactory(this); + frameHandlerFactory = new JettyWebSocketFrameHandlerFactory(this, components); sessionListeners.add(sessionTracker); addBean(sessionTracker); } diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java index db9f34bb039..d3235ad0ed7 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java @@ -169,9 +169,6 @@ public class JettyWebSocketFrameHandler implements FrameHandler if (binaryHandle != null) binarySink = JettyWebSocketFrameHandlerFactory.createMessageSink(binaryHandle, binarySinkClass, executor, session); - // Decorate the endpointInstance directly before calling the onOpen method. - coreSession.getWebSocketComponents().getObjectFactory().decorate(endpointInstance); - if (openHandle != null) openHandle.invoke(); diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java index 4746ebd4f4f..b16eba885ef 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java @@ -46,6 +46,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.InvalidWebSocketException; import org.eclipse.jetty.websocket.core.internal.messages.ByteArrayMessageSink; @@ -78,11 +79,18 @@ import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils; public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle { private final WebSocketContainer container; - private Map, JettyWebSocketFrameHandlerMetadata> metadataMap = new ConcurrentHashMap<>(); + private final WebSocketComponents components; + private final Map, JettyWebSocketFrameHandlerMetadata> metadataMap = new ConcurrentHashMap<>(); - public JettyWebSocketFrameHandlerFactory(WebSocketContainer container) + public JettyWebSocketFrameHandlerFactory(WebSocketContainer container, WebSocketComponents components) { this.container = container; + this.components = components; + } + + public WebSocketComponents getWebSocketComponents() + { + return components; } public JettyWebSocketFrameHandlerMetadata getMetadata(Class endpointClass) @@ -130,6 +138,9 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle final MethodHandle pongHandle = InvokerUtils.bindTo(metadata.getPongHandle(), endpointInstance); BatchMode batchMode = metadata.getBatchMode(); + // Decorate the endpointInstance while we are still upgrading for access to things like HttpSession. + components.getObjectFactory().decorate(endpointInstance); + return new JettyWebSocketFrameHandler( container, endpointInstance, diff --git a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java index 5cf8a32268c..1fd9853c81e 100644 --- a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java +++ b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerTest.java @@ -59,11 +59,10 @@ public class JettyWebSocketFrameHandlerTest container.stop(); } - private final JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container); + private final WebSocketComponents components = new WebSocketComponents(); + private final JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container, components); private final CoreSession coreSession = new CoreSession.Empty() { - private final WebSocketComponents components = new WebSocketComponents(); - @Override public Behavior getBehavior() { diff --git a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java index 2a3e64b5ea0..212cad932f4 100644 --- a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java +++ b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.websocket.common; import org.eclipse.jetty.websocket.api.exceptions.InvalidWebSocketException; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.DuplicateAnnotationException; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.internal.messages.ByteArrayMessageSink; @@ -51,7 +52,8 @@ public class LocalEndpointMetadataTest container.stop(); } - private JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container); + private final WebSocketComponents components = new WebSocketComponents(); + private final JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container,components); private JettyWebSocketFrameHandlerMetadata createMetadata(Class endpointClass) { diff --git a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java index ce5094845ad..808939ec9b9 100644 --- a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java +++ b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java @@ -110,7 +110,7 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements this.webSocketMappings = webSocketMappings; this.components = components; this.executor = executor; - this.frameHandlerFactory = new JettyServerFrameHandlerFactory(this); + this.frameHandlerFactory = new JettyServerFrameHandlerFactory(this, components); addBean(frameHandlerFactory); addSessionListener(sessionTracker); diff --git a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java index d1ce713e73f..fad26c8d0f3 100644 --- a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java +++ b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java @@ -15,10 +15,10 @@ package org.eclipse.jetty.websocket.server.internal; import javax.servlet.ServletContext; -import org.eclipse.jetty.websocket.api.WebSocketContainer; import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandler; import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory; import org.eclipse.jetty.websocket.core.FrameHandler; +import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory; import org.eclipse.jetty.websocket.core.server.ServerUpgradeRequest; import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; @@ -32,9 +32,9 @@ public class JettyServerFrameHandlerFactory extends JettyWebSocketFrameHandlerFa return (container == null) ? null : container.getBean(JettyServerFrameHandlerFactory.class); } - public JettyServerFrameHandlerFactory(WebSocketContainer container) + public JettyServerFrameHandlerFactory(JettyWebSocketServerContainer container, WebSocketComponents components) { - super(container); + super(container, components); } @Override diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java index ba532296d9f..ea4559d29dd 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.net.URI; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; @@ -160,6 +161,22 @@ public class JavaxWebSocketCdiTest assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); } + @Test + public void testHttpSessionInjection() throws Exception + { + start((servletContext, wsContainer) -> wsContainer.addEndpoint(CdiHttpSessionSocket.class)); + + // If we can get an echo from the websocket endpoint we know that CDI injection of the logger worked as there was no NPE. + AnnotatedCdiClientSocket clientEndpoint = new AnnotatedCdiClientSocket(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/echo"); + Session session = _client.connectToServer(clientEndpoint, uri); + session.getBasicRemote().sendObject("hello world"); + String rcvMessage = clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS); + assertThat(rcvMessage, is("hello world")); + session.close(); + assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); + } + public static class ClientConfigurator extends ClientEndpointConfig.Configurator { @Inject @@ -222,9 +239,8 @@ public class JavaxWebSocketCdiTest public static class AnnotatedCdiEchoSocket { @Inject - public Logger logger; - - private Session session; + protected Logger logger; + protected Session session; @OnOpen public void onOpen(Session session) @@ -370,4 +386,18 @@ public class JavaxWebSocketCdiTest public static class AnnotatedEncoderDecoderClientSocket extends AnnotatedCdiClientSocket { } + + @ServerEndpoint("/echo") + public static class CdiHttpSessionSocket extends AnnotatedCdiEchoSocket + { + @Inject + private javax.servlet.http.HttpSession httpSession; + + @Override + public void onMessage(String message) throws IOException + { + Objects.requireNonNull(httpSession); + session.getBasicRemote().sendText(message + " " + httpSession.getClass()); + } + } } From 045bc72c921e21f55028f66c0ff328b95535da45 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 15 Apr 2021 09:38:40 +1000 Subject: [PATCH 8/8] Disable test for WebSocket CDI HttpSession injection and link to issue. Signed-off-by: Lachlan Roberts --- .../websocket/common/LocalEndpointMetadataTest.java | 2 +- .../jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java index 212cad932f4..de14ec98537 100644 --- a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java +++ b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/LocalEndpointMetadataTest.java @@ -53,7 +53,7 @@ public class LocalEndpointMetadataTest } private final WebSocketComponents components = new WebSocketComponents(); - private final JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container,components); + private final JettyWebSocketFrameHandlerFactory endpointFactory = new JettyWebSocketFrameHandlerFactory(container, components); private JettyWebSocketFrameHandlerMetadata createMetadata(Class endpointClass) { diff --git a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java index ea4559d29dd..c32fbfafd60 100644 --- a/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java +++ b/tests/test-cdi/src/test/java/org/eclipse/jetty/cdi/tests/websocket/JavaxWebSocketCdiTest.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.net.URI; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; @@ -56,9 +55,11 @@ import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletCont import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -162,6 +163,7 @@ public class JavaxWebSocketCdiTest } @Test + @Disabled("See issue https://github.com/eclipse/jetty.project/issues/6174") public void testHttpSessionInjection() throws Exception { start((servletContext, wsContainer) -> wsContainer.addEndpoint(CdiHttpSessionSocket.class)); @@ -172,7 +174,7 @@ public class JavaxWebSocketCdiTest Session session = _client.connectToServer(clientEndpoint, uri); session.getBasicRemote().sendObject("hello world"); String rcvMessage = clientEndpoint._textMessages.poll(5, TimeUnit.SECONDS); - assertThat(rcvMessage, is("hello world")); + assertThat(rcvMessage, containsString("hello world, SessionID:")); session.close(); assertTrue(clientEndpoint._closeLatch.await(5, TimeUnit.SECONDS)); } @@ -396,8 +398,7 @@ public class JavaxWebSocketCdiTest @Override public void onMessage(String message) throws IOException { - Objects.requireNonNull(httpSession); - session.getBasicRemote().sendText(message + " " + httpSession.getClass()); + session.getBasicRemote().sendText(message + ", SessionID:" + httpSession.getId()); } } }