diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc index 27f89762527d..da2fa1a6ff23 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc @@ -59,6 +59,9 @@ xref:pg-websocket-endpoints-listener[Listener endpoints] are notified of events xref:pg-websocket-endpoints-annotated[Annotated endpoints] are notified of events by invoking the correspondent method annotated with the correspondent annotation from the `+org.eclipse.jetty.websocket.api.annotations.*+` package. +Jetty uses ``MethodHandle``s to instantiate WebSocket endpoints and invoke WebSocket event methods, so WebSocket endpoint classes and WebSocket event methods must be `public`. +This guarantees that WebSocket endpoints can be accessed by the Jetty implementation without additional configuration also when your application uses the Java Module System (JPMS). + For both types of WebSocket endpoints, only one thread at a time will be delivering frame or message events to the corresponding methods; the next frame or message event will not be delivered until the previous call to the corresponding method has exited, and if there is xref:pg-websocket-endpoints-demand[demand] for it. Endpoints will always be notified of message events in the same order they were received over the network. @@ -152,29 +155,6 @@ include::{doc_code}/org/eclipse/jetty/docs/programming/WebSocketDocs.java[tags=s A WebSocket endpoint may annotate methods with `+org.eclipse.jetty.websocket.api.annotations.*+` annotations to receive WebSocket events. -Jetty uses ``MethodHandle``s to instantiate WebSocket endpoints and invoke WebSocket event methods, so WebSocket endpoint classes and WebSocket event methods must be `public`. - -When using JPMS, you must ensure that the Jetty JPMS module `org.eclipse.jetty.websocket.common` can _read_ (in JPMS terms) the WebSocket endpoint classes in your JPMS module. - -For example, your application may use the Jetty WebSocket client so that the JPMS module that contains your WebSocket endpoint classes looks like this: - -[source,java] -.module-info.java ----- -module com.acme.websocket -{ - // The Jetty WebSocket client dependency. - requires org.eclipse.jetty.websocket.client; -} ----- - -To ensure that Jetty _reads_ your JPMS module, you must start the JVM with the following option: - -[source] ----- -$ java --add-reads org.eclipse.jetty.websocket.common=com.acme.websocket ... ----- - Each annotated event method may take an optional `Session` argument as its first parameter: [source,java,indent=0] diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java index aa010794a536..b342d50233ac 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java @@ -162,7 +162,7 @@ private JettyWebSocketFrameHandlerMetadata createListenerMetadata(Class endpo JettyWebSocketFrameHandlerMetadata metadata = new JettyWebSocketFrameHandlerMetadata(); metadata.setAutoDemand(Session.Listener.AutoDemanding.class.isAssignableFrom(endpointClass)); - MethodHandles.Lookup lookup = JettyWebSocketFrameHandlerFactory.getServerMethodHandleLookup(); + MethodHandles.Lookup lookup = getApplicationMethodHandleLookup(endpointClass); Method openMethod = findMethod(endpointClass, "onWebSocketOpen", Session.class); if (openMethod != null) @@ -244,19 +244,14 @@ private Method findMethod(Class klass, String name, Class... parameters) Method method = ReflectUtils.findMethod(klass, name, parameters); if (method == null) return null; - if (!isOverridden(method)) - return null; - // The method is overridden, but it may be declared in a non-public - // class, for example an anonymous class, where it won't be accessible, - // therefore replace it with the accessible version from Session.Listener. - if (!Modifier.isPublic(klass.getModifiers())) - method = ReflectUtils.findMethod(Session.Listener.class, name, parameters); - return method; + if (isOverridden(method)) + return method; + return null; } private boolean isOverridden(Method method) { - return method != null && method.getDeclaringClass() != Session.Listener.class; + return method.getDeclaringClass() != Session.Listener.class; } private JettyWebSocketFrameHandlerMetadata createAnnotatedMetadata(WebSocket anno, Class endpointClass) @@ -265,10 +260,9 @@ private JettyWebSocketFrameHandlerMetadata createAnnotatedMetadata(WebSocket ann metadata.setAutoDemand(anno.autoDemand()); MethodHandles.Lookup lookup = getApplicationMethodHandleLookup(endpointClass); - Method onmethod; // OnWebSocketOpen [0..1] - onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketOpen.class); + Method onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketOpen.class); if (onmethod != null) { assertSignatureValid(endpointClass, onmethod, OnWebSocketOpen.class); diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/CloseTrackingEndpoint.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/CloseTrackingEndpoint.java index ebc6453f6130..f1832d333910 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/CloseTrackingEndpoint.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/CloseTrackingEndpoint.java @@ -44,7 +44,7 @@ public class CloseTrackingEndpoint extends Session.Listener.AbstractAutoDemandin public String closeReason = null; public CountDownLatch closeLatch = new CountDownLatch(1); public AtomicInteger closeCount = new AtomicInteger(0); - public CountDownLatch connectLatch = new CountDownLatch(1); + public CountDownLatch openLatch = new CountDownLatch(1); public CountDownLatch errorLatch = new CountDownLatch(1); public LinkedBlockingQueue messageQueue = new LinkedBlockingQueue<>(); @@ -94,7 +94,7 @@ public void onWebSocketOpen(Session session) { super.onWebSocketOpen(session); LOG.debug("onWebSocketOpen({})", session); - connectLatch.countDown(); + openLatch.countDown(); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java index 5234e1da46cc..63fd6027008a 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java @@ -89,7 +89,7 @@ private E assertExpectedError(ExecutionException e, CloseT assertThat("Error", capcause, errorMatcher); // Validate that websocket didn't see an open event - assertThat("Open Latch", wsocket.connectLatch.getCount(), is(1L)); + assertThat("Open Latch", wsocket.openLatch.getCount(), is(1L)); // Return the captured cause return (E)capcause; diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ConnectFutureTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ConnectFutureTest.java index 019666276d4e..7fbfad66a0df 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ConnectFutureTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ConnectFutureTest.java @@ -113,7 +113,7 @@ public void testAbortDuringCreator() throws Exception assertTrue(connect.cancel(true)); assertThrows(CancellationException.class, () -> connect.get(5, TimeUnit.SECONDS)); exitCreator.countDown(); - assertFalse(clientSocket.connectLatch.await(1, TimeUnit.SECONDS)); + assertFalse(clientSocket.openLatch.await(1, TimeUnit.SECONDS)); Throwable error = clientSocket.error.get(); assertThat(error, instanceOf(UpgradeException.class)); @@ -155,7 +155,7 @@ public void onWebSocketSessionCreated(Session session) assertTrue(connect.cancel(true)); assertThrows(CancellationException.class, () -> connect.get(5, TimeUnit.SECONDS)); exitListener.countDown(); - assertTrue(clientSocket.connectLatch.await(5, TimeUnit.SECONDS)); + assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS)); assertTrue(clientSocket.errorLatch.await(5, TimeUnit.SECONDS)); assertThat(clientSocket.error.get(), instanceOf(CancellationException.class)); } @@ -194,7 +194,7 @@ public void onHandshakeResponse(Request request, Response response) assertTrue(connect.cancel(true)); assertThrows(CancellationException.class, () -> connect.get(5, TimeUnit.SECONDS)); exitListener.countDown(); - assertTrue(clientSocket.connectLatch.await(5, TimeUnit.SECONDS)); + assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS)); assertTrue(clientSocket.errorLatch.await(5, TimeUnit.SECONDS)); assertThat(clientSocket.error.get(), instanceOf(CancellationException.class)); } @@ -205,29 +205,14 @@ public void testAbortOnOpened() throws Exception start(wsHandler -> wsHandler.getServerWebSocketContainer().addMapping("/", (upgradeRequest, upgradeResponse, callback) -> new EchoSocket())); - CountDownLatch exitOnConnect = new CountDownLatch(1); - CloseTrackingEndpoint clientSocket = new CloseTrackingEndpoint() - { - @Override - public void onWebSocketOpen(Session session) - { - try - { - super.onWebSocketOpen(session); - exitOnConnect.await(); - } - catch (InterruptedException e) - { - throw new IllegalStateException(e); - } - } - }; + CountDownLatch exitOnOpen = new CountDownLatch(1); + AwaitOnOpen clientSocket = new AwaitOnOpen(exitOnOpen); // Abort during the call to onOpened. This is after the connection upgrade, but before future completion. Future connect = client.connect(clientSocket, WSURI.toWebsocket(server.getURI())); - assertTrue(clientSocket.connectLatch.await(5, TimeUnit.SECONDS)); + assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS)); assertTrue(connect.cancel(true)); - exitOnConnect.countDown(); + exitOnOpen.countDown(); // We got an error on the WebSocket endpoint and an error from the future. assertTrue(clientSocket.errorLatch.await(5, TimeUnit.SECONDS)); @@ -245,7 +230,7 @@ public void testAbortAfterCompletion() throws Exception Session session = connect.get(5, TimeUnit.SECONDS); // If we can send and receive messages the future has been completed. - assertTrue(clientSocket.connectLatch.await(5, TimeUnit.SECONDS)); + assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS)); Session session1 = clientSocket.getSession(); session1.sendText("hello", Callback.NOOP); assertThat(clientSocket.messageQueue.poll(5, TimeUnit.SECONDS), Matchers.is("hello")); @@ -339,29 +324,14 @@ public void testAbortWithExceptionAfterUpgrade() throws Exception start(wsHandler -> wsHandler.getServerWebSocketContainer().addMapping("/", (upgradeRequest, upgradeResponse, callback) -> new EchoSocket())); - CountDownLatch exitOnConnect = new CountDownLatch(1); - CloseTrackingEndpoint clientSocket = new CloseTrackingEndpoint() - { - @Override - public void onWebSocketOpen(Session session) - { - try - { - super.onWebSocketOpen(session); - exitOnConnect.await(); - } - catch (InterruptedException e) - { - throw new IllegalStateException(e); - } - } - }; + CountDownLatch exitOnOpen = new CountDownLatch(1); + AwaitOnOpen clientSocket = new AwaitOnOpen(exitOnOpen); // Complete the CompletableFuture with an exception the during the call to onOpened. CompletableFuture connect = client.connect(clientSocket, WSURI.toWebsocket(server.getURI())); - assertTrue(clientSocket.connectLatch.await(5, TimeUnit.SECONDS)); + assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS)); assertTrue(connect.completeExceptionally(new WebSocketException("custom exception"))); - exitOnConnect.countDown(); + exitOnOpen.countDown(); // Exception from the future is correct. ExecutionException futureError = assertThrows(ExecutionException.class, () -> connect.get(5, TimeUnit.SECONDS)); @@ -375,4 +345,28 @@ public void onWebSocketOpen(Session session) assertThat(endpointError, instanceOf(WebSocketException.class)); assertThat(endpointError.getMessage(), is("custom exception")); } + + public static class AwaitOnOpen extends CloseTrackingEndpoint + { + private final CountDownLatch exitOnOpen; + + public AwaitOnOpen(CountDownLatch latch) + { + exitOnOpen = latch; + } + + @Override + public void onWebSocketOpen(Session session) + { + try + { + super.onWebSocketOpen(session); + exitOnOpen.await(); + } + catch (InterruptedException e) + { + throw new IllegalStateException(e); + } + } + } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java index 39e49d6095c5..8de2b8af6818 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java @@ -385,7 +385,7 @@ public void testLocalRemoteAddress() throws Exception try (Session ignored = future.get(5, TimeUnit.SECONDS)) { - Assertions.assertTrue(cliSock.connectLatch.await(1, TimeUnit.SECONDS)); + Assertions.assertTrue(cliSock.openLatch.await(1, TimeUnit.SECONDS)); InetSocketAddress local = (InetSocketAddress)cliSock.getSession().getLocalSocketAddress(); InetSocketAddress remote = (InetSocketAddress)cliSock.getSession().getRemoteSocketAddress(); diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java index b425c1ac5098..d1024217affb 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java @@ -16,8 +16,6 @@ import java.net.URI; import java.nio.ByteBuffer; import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,7 +23,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.api.Callback; import org.eclipse.jetty.websocket.api.Session; @@ -42,7 +39,9 @@ import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class WebSocketListenerTest @@ -130,44 +129,18 @@ public void testBinaryListeners(Class clazz) throws Exception } @Test - public void testAnonymousListener() throws Exception + public void testAnonymousListener() { - CountDownLatch openLatch = new CountDownLatch(1); - CountDownLatch closeLatch = new CountDownLatch(1); - BlockingQueue textMessages = new BlockingArrayQueue<>(); Session.Listener clientEndpoint = new Session.Listener.AutoDemanding() { @Override public void onWebSocketOpen(Session session) { - openLatch.countDown(); - } - - @Override - public void onWebSocketText(String message) - { - textMessages.add(message); - } - - @Override - public void onWebSocketClose(int statusCode, String reason) - { - closeLatch.countDown(); } }; - - Session session = client.connect(clientEndpoint, serverUri.resolve("/echo")).get(5, TimeUnit.SECONDS); - assertTrue(openLatch.await(5, TimeUnit.SECONDS)); - - // Send and receive echo on client. - String payload = "hello world"; - session.sendText(payload, Callback.NOOP); - String echoMessage = textMessages.poll(5, TimeUnit.SECONDS); - assertThat(echoMessage, is(payload)); - - // Close normally. - session.close(StatusCode.NORMAL, "standard close", Callback.NOOP); - assertTrue(closeLatch.await(5, TimeUnit.SECONDS)); + Exception failure = assertThrows(Exception.class, () -> client.connect(clientEndpoint, serverUri.resolve("/echo")).get(5, TimeUnit.SECONDS)); + // The endpoint class is not public. + assertThat(failure.getCause(), instanceOf(IllegalAccessException.class)); } private List> getClassListFromArguments(Stream stream) diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AbstractCloseEndpoint.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AbstractCloseEndpoint.java index 1d65952727cf..6591abe9847d 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AbstractCloseEndpoint.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/AbstractCloseEndpoint.java @@ -29,7 +29,7 @@ public abstract class AbstractCloseEndpoint extends Session.Listener.AbstractAutoDemanding { public final Logger log; - public CountDownLatch connectLatch = new CountDownLatch(1); + public CountDownLatch openLatch = new CountDownLatch(1); public CountDownLatch closeLatch = new CountDownLatch(1); public String closeReason = null; public int closeStatusCode = -1; @@ -45,7 +45,7 @@ public void onWebSocketOpen(Session sess) { super.onWebSocketOpen(sess); log.debug("onWebSocketOpen({})", sess); - connectLatch.countDown(); + openLatch.countDown(); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DirectUpgradeTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DirectUpgradeTest.java index b1ed4b503ca0..18da2bfb57f9 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DirectUpgradeTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/DirectUpgradeTest.java @@ -203,7 +203,7 @@ public boolean handle(Request request, Response response, Callback callback) assertEquals("HELLO", response.getContentAsString()); } - private static class EchoListener implements Session.Listener + public static class EchoListener implements Session.Listener { private Session session; diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java index 168d1c173d71..5ac6e5783efd 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java @@ -269,7 +269,7 @@ public void testSecondCloseFromOnClosed() throws Exception // Hard close from the server. Server onClosed() will try to close again which should be a NOOP. AbstractCloseEndpoint serverEndpoint = serverEndpointCreator.pollLastCreated(); - assertTrue(serverEndpoint.connectLatch.await(5, SECONDS)); + assertTrue(serverEndpoint.openLatch.await(5, SECONDS)); Session session = serverEndpoint.getSession(); session.close(StatusCode.SHUTDOWN, "SHUTDOWN hard close", Callback.NOOP); @@ -294,7 +294,7 @@ public void testSecondCloseFromOnClosedInNewThread() throws Exception // Hard close from the server. Server onClosed() will try to close again which should be a NOOP. AbstractCloseEndpoint serverEndpoint = serverEndpointCreator.pollLastCreated(); - assertTrue(serverEndpoint.connectLatch.await(5, SECONDS)); + assertTrue(serverEndpoint.openLatch.await(5, SECONDS)); Session session = serverEndpoint.getSession(); session.close(StatusCode.SHUTDOWN, "SHUTDOWN hard close", Callback.NOOP); diff --git a/tests/jetty-testers/src/main/java/org/eclipse/jetty/tests/testers/MavenHelper.java b/tests/jetty-testers/src/main/java/org/eclipse/jetty/tests/testers/MavenHelper.java index be66b8083825..0d4453db3275 100644 --- a/tests/jetty-testers/src/main/java/org/eclipse/jetty/tests/testers/MavenHelper.java +++ b/tests/jetty-testers/src/main/java/org/eclipse/jetty/tests/testers/MavenHelper.java @@ -73,7 +73,8 @@ private DefaultRepositorySystemSession newRepositorySystemSession(RepositorySyst private static RemoteRepository newCentralRepository() { - return new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/").build(); + String centralRepository = System.getProperty("maven.repo.uri", "https://repo.maven.apache.org/maven2/"); + return new RemoteRepository.Builder("central", "default", centralRepository).build(); } private static class LogTransferListener extends AbstractTransferListener diff --git a/tests/pom.xml b/tests/pom.xml index 42b9e5021bc3..9be5b510e8e4 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -16,7 +16,7 @@ jetty-test-session-common test-distribution test-integration - + test-jpms true diff --git a/tests/test-jpms/pom.xml b/tests/test-jpms/pom.xml index 37e0c5710e8c..bb11bdc5636f 100644 --- a/tests/test-jpms/pom.xml +++ b/tests/test-jpms/pom.xml @@ -4,31 +4,44 @@ org.eclipse.jetty.tests tests - 12.0.5-SNAPSHOT + 12.0.6-SNAPSHOT test-jpms - pom Tests :: JPMS - - test-jpms-websocket-core - org.eclipse.jetty jetty-slf4j-impl - test - org.eclipse.jetty.toolchain - jetty-test-helper - test + org.eclipse.jetty.tests + jetty-testers - org.junit.jupiter - junit-jupiter - test + org.eclipse.jetty.websocket + jetty-websocket-jetty-client + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-server + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${session.repositorySession.localRepository.basedir.absolutePath} + ${project.version} + ${slf4j.version} + + + + + + diff --git a/tests/test-jpms/src/test/java/org/eclipse/jetty/tests/jpms/JPMSWebSocketTest.java b/tests/test-jpms/src/test/java/org/eclipse/jetty/tests/jpms/JPMSWebSocketTest.java new file mode 100644 index 000000000000..447f5e0fc191 --- /dev/null +++ b/tests/test-jpms/src/test/java/org/eclipse/jetty/tests/jpms/JPMSWebSocketTest.java @@ -0,0 +1,155 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.tests.jpms; + +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.tests.testers.JPMSTester; +import org.eclipse.jetty.tests.testers.Tester; +import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.websocket.api.Callback; +import org.eclipse.jetty.websocket.api.Session; +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.WebSocketUpgradeHandler; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(WorkDirExtension.class) +public class JPMSWebSocketTest +{ + @Test + public void testJPMSWebSocket(WorkDir workDir) throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + String slf4jVersion = System.getProperty("slf4jVersion"); + + int port = Tester.freePort(); + try (JPMSTester server = new JPMSTester.Builder(workDir.getPath()) +// .jvmArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005") + .classesDirectory(MavenPaths.targetDir().resolve("test-classes")) + .moduleInfo(""" + module app.server + { + requires org.eclipse.jetty.websocket.server; + exports org.eclipse.jetty.tests.jpms; + } + """) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-jetty-server:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-jetty-api:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-server:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-http:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-io:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-util:" + jettyVersion) + .addToModulePath("org.slf4j:slf4j-api:" + slf4jVersion) + .addToModulePath("org.eclipse.jetty:jetty-slf4j-impl:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-jetty-common:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-core-server:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-core-common:" + jettyVersion) + .mainClass(ServerMain.class) + .args(String.valueOf(port)) + .build()) + { + assertTrue(server.awaitConsoleLogsFor("Started oejs.Server@", Duration.ofSeconds(10))); + + try (JPMSTester client = new JPMSTester.Builder(workDir.getPath()) + .classesDirectory(MavenPaths.targetDir().resolve("test-classes")) + .moduleInfo(""" + module app.client + { + requires org.eclipse.jetty.websocket.client; + exports org.eclipse.jetty.tests.jpms; + } + """) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-jetty-client:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-jetty-api:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-client:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-alpn-client:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-http:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-io:" + jettyVersion) + .addToModulePath("org.eclipse.jetty:jetty-util:" + jettyVersion) + .addToModulePath("org.slf4j:slf4j-api:" + slf4jVersion) + .addToModulePath("org.eclipse.jetty:jetty-slf4j-impl:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-jetty-common:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-core-client:" + jettyVersion) + .addToModulePath("org.eclipse.jetty.websocket:jetty-websocket-core-common:" + jettyVersion) + .mainClass(ClientMain.class) + .args(String.valueOf(port)) + .build()) + { + assertTrue(client.awaitConsoleLogsFor("SUCCESS", Duration.ofSeconds(10))); + } + } + } + + public static class ServerMain + { + public static void main(String[] args) throws Exception + { + Server server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(Integer.parseInt(args[0])); + server.addConnector(connector); + WebSocketUpgradeHandler wsHandler = WebSocketUpgradeHandler.from(server, container -> + container.addMapping("/echo", (request, response, callback) -> new AnnotatedServerEndPoint()) + ); + server.setHandler(wsHandler); + server.start(); + } + } + + @WebSocket + public static class AnnotatedServerEndPoint + { + @OnWebSocketMessage + public void onText(Session session, String text) + { + session.sendText(text, Callback.NOOP); + } + } + + public static class ClientMain + { + public static void main(String[] args) throws Exception + { + WebSocketClient client = new WebSocketClient(); + client.start(); + + int port = Integer.parseInt(args[0]); + ListenerClientEndPoint endPoint = new ListenerClientEndPoint(); + Session session = client.connect(endPoint, URI.create("ws://localhost:" + port + "/echo")).get(5, TimeUnit.SECONDS); + session.sendText("hello", Callback.NOOP); + endPoint.thenRun(() -> System.err.println("SUCCESS")); + } + } + + public static class ListenerClientEndPoint extends CompletableFuture implements Session.Listener.AutoDemanding + { + @Override + public void onWebSocketText(String message) + { + complete(null); + } + } +} diff --git a/tests/test-jpms/src/test/resources/jetty-logging.properties b/tests/test-jpms/src/test/resources/jetty-logging.properties new file mode 100644 index 000000000000..c291cb899dec --- /dev/null +++ b/tests/test-jpms/src/test/resources/jetty-logging.properties @@ -0,0 +1 @@ +org.eclipse.jetty.LEVEL=INFO diff --git a/tests/test-jpms/test-jpms-websocket-core/pom.xml b/tests/test-jpms/test-jpms-websocket-core/pom.xml deleted file mode 100644 index 36106f069ef3..000000000000 --- a/tests/test-jpms/test-jpms-websocket-core/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - test-jpms - org.eclipse.jetty.tests - 12.0.5-SNAPSHOT - - 4.0.0 - test-jpms-websocket-core - jar - Tests :: JPMS :: Core WebSocket - - - - org.eclipse.jetty.websocket - jetty-websocket-core-server - - - org.eclipse.jetty.websocket - jetty-websocket-core-client - - - diff --git a/tests/test-jpms/test-jpms-websocket-core/src/main/java/module-info.java b/tests/test-jpms/test-jpms-websocket-core/src/main/java/module-info.java deleted file mode 100644 index 46fa94825419..000000000000 --- a/tests/test-jpms/test-jpms-websocket-core/src/main/java/module-info.java +++ /dev/null @@ -1,22 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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 -// ======================================================================== -// - -/** - * This module-info.java exists so that the tests can be run in JPMS mode, - * therefore testing the JPMS module descriptors of the dependencies involved. - */ -module org.eclipse.jetty.websocket.core.tests -{ - requires org.eclipse.jetty.websocket.core.client; - requires org.eclipse.jetty.websocket.core.server; -} diff --git a/tests/test-jpms/test-jpms-websocket-core/src/test/java/WebSocketCoreJPMSTest.java b/tests/test-jpms/test-jpms-websocket-core/src/test/java/WebSocketCoreJPMSTest.java deleted file mode 100644 index 45bd16979fa2..000000000000 --- a/tests/test-jpms/test-jpms-websocket-core/src/test/java/WebSocketCoreJPMSTest.java +++ /dev/null @@ -1,115 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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 -// ======================================================================== -// - -import java.net.URI; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.util.Callback; -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.FrameHandler; -import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest; -import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient; -import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; -import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class WebSocketCoreJPMSTest -{ - private Server _server; - private ServerConnector _serverConnector; - private WebSocketCoreClient _client; - - @BeforeEach - public void before() throws Exception - { - _server = new Server(); - _serverConnector = new ServerConnector(_server); - _server.addConnector(_serverConnector); - - WebSocketUpgradeHandler webSocketUpgradeHandler = new WebSocketUpgradeHandler(); - FrameHandler myFrameHandler = new TestFrameHandler("Server"); - webSocketUpgradeHandler.addMapping("/ws", WebSocketNegotiator.from(negotiation -> myFrameHandler)); - - _server.setHandler(webSocketUpgradeHandler); - _server.start(); - - _client = new WebSocketCoreClient(); - _client.start(); - } - - @AfterEach - public void after() throws Exception - { - _client.stop(); - _server.stop(); - } - - @Test - public void testSimpleEcho() throws Exception - { - TestFrameHandler frameHandler = new TestFrameHandler("Client"); - URI uri = URI.create("ws://localhost:" + _serverConnector.getLocalPort() + "/ws"); - CoreClientUpgradeRequest upgradeRequest = CoreClientUpgradeRequest.from(_client, uri, frameHandler); - upgradeRequest.addExtensions("permessage-deflate"); - CoreSession coreSession = _client.connect(upgradeRequest).get(5, TimeUnit.SECONDS); - coreSession.close(Callback.NOOP); - } - - public static class TestFrameHandler implements FrameHandler - { - private static final Logger LOG = LoggerFactory.getLogger(TestFrameHandler.class); - - private final String _id; - - public TestFrameHandler(String id) - { - _id = id; - } - - @Override - public void onOpen(CoreSession coreSession, Callback callback) - { - LOG.info(_id + " onOpen"); - callback.succeeded(); - } - - @Override - public void onFrame(Frame frame, Callback callback) - { - LOG.info(_id + " onFrame"); - callback.succeeded(); - } - - @Override - public void onError(Throwable cause, Callback callback) - { - LOG.info(_id + " onError"); - callback.succeeded(); - } - - @Override - public void onClosed(CloseStatus closeStatus, Callback callback) - { - LOG.info(_id + " onClosed"); - callback.succeeded(); - } - } -}