Merge pull request #4522 from eclipse/jetty-10.0.x-4226-JavaxWebSocketJPMS

Issue #4226 - fix JPMS issues with javax websockets
This commit is contained in:
Lachlan 2020-01-30 12:09:02 +11:00 committed by GitHub
commit 1b10e2330e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 207 additions and 90 deletions

View File

@ -41,9 +41,9 @@ public abstract class JavaxWebSocketContainer extends ContainerLifeCycle impleme
{
private static final Logger LOG = Log.getLogger(JavaxWebSocketContainer.class);
private final SessionTracker sessionTracker = new SessionTracker();
private List<JavaxWebSocketSessionListener> sessionListeners = new ArrayList<>();
protected Configuration.ConfigurationCustomizer defaultCustomizer = new Configuration.ConfigurationCustomizer();
protected WebSocketComponents components;
private final List<JavaxWebSocketSessionListener> sessionListeners = new ArrayList<>();
protected final Configuration.ConfigurationCustomizer defaultCustomizer = new Configuration.ConfigurationCustomizer();
protected final WebSocketComponents components;
public JavaxWebSocketContainer(WebSocketComponents components)
{

View File

@ -29,14 +29,6 @@ import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders;
public class JavaxWebSocketFrameHandlerMetadata
{
/**
* Constant for "unset" @OnMessage annotation values.
* <p>
* (-2 means unset/undeclared, -1 means whatever that value means, such as: no idletimeout, or no maximum message size limit)
* </p>
*/
public static final int UNSET = -2;
private static final String[] NO_VARIABLES = new String[0];
// EndpointConfig entries
@ -228,6 +220,8 @@ public class JavaxWebSocketFrameHandlerMetadata
public static class MessageMetadata
{
private static final int UNSET = -1;
public MethodHandle handle;
public Class<? extends MessageSink> sinkClass;
public AvailableDecoders.RegisteredDecoder registeredDecoder;
@ -249,7 +243,7 @@ public class JavaxWebSocketFrameHandlerMetadata
public boolean isMaxMessageSizeSet()
{
return (maxMessageSize != UNSET) && (maxMessageSize != 0);
return maxMessageSize != UNSET;
}
}
}

View File

@ -148,7 +148,7 @@ public class JavaxWebSocketServletContainerInitializer implements ServletContain
* @param context the context to work with
* @return the default {@link ServerContainer} for this context
*/
public static JavaxWebSocketServerContainer initialize(ServletContextHandler context)
public static ServerContainer initialize(ServletContextHandler context)
{
JavaxWebSocketServerContainer serverContainer = JavaxWebSocketServerContainer.getContainer(context.getServletContext());
if (serverContainer == null)
@ -180,7 +180,7 @@ public class JavaxWebSocketServletContainerInitializer implements ServletContain
}
ServletContextHandler servletContextHandler = ServletContextHandler.getServletContextHandler(context, "Javax WebSocket SCI");
JavaxWebSocketServerContainer container = initialize(servletContextHandler);
ServerContainer container = initialize(servletContextHandler);
try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(context.getClassLoader()))
{

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.websocket.javax.tests;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import javax.websocket.ClientEndpoint;
@ -44,7 +45,8 @@ public class EventSocket
public Session session;
public EndpointConfig endpointConfig;
public BlockingQueue<String> messageQueue = new BlockingArrayQueue<>();
public BlockingQueue<String> textMessages = new BlockingArrayQueue<>();
public BlockingQueue<ByteBuffer> binaryMessages = new BlockingArrayQueue<>();
public volatile Throwable error = null;
public volatile CloseReason closeReason = null;
@ -67,7 +69,15 @@ public class EventSocket
{
if (LOG.isDebugEnabled())
LOG.debug("{} onMessage(): {}", toString(), message);
messageQueue.offer(message);
textMessages.offer(message);
}
@OnMessage
public void onMessage(ByteBuffer message) throws IOException
{
if (LOG.isDebugEnabled())
LOG.debug("{} onMessage(): {}", toString(), message);
binaryMessages.offer(message);
}
@OnClose

View File

@ -140,7 +140,7 @@ public class JettySpecificConfigTest
// Send and receive an echo.
session.getBasicRemote().sendText("echo");
String resp = clientEndpoint.messageQueue.poll(1, TimeUnit.SECONDS);
String resp = clientEndpoint.textMessages.poll(1, TimeUnit.SECONDS);
assertThat("Response echo", resp, is("echo"));
// Close the Session.

View File

@ -96,7 +96,7 @@ public class PathParamTest
Session session = container.connectToServer(clientEndpoint, serverUri);
session.getBasicRemote().sendText("echo");
String resp = clientEndpoint.messageQueue.poll(1, TimeUnit.SECONDS);
String resp = clientEndpoint.textMessages.poll(1, TimeUnit.SECONDS);
assertThat("Response echo", resp, is("echo-myParam"));
session.close();
clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS);

View File

@ -0,0 +1,137 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.tests;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.ContainerProvider;
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.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ServerConfigTest
{
private Server server;
private WebSocketContainer client;
private ServerConnector connector;
private static final long idleTimeout = 500;
private static final int maxTextMessageSize = 50;
private static final int maxBinaryMessageSize = 60;
private static final long asyncSendTimeout = 200;
@BeforeEach
public void start() throws Exception
{
server = new Server();
connector = new ServerConnector(server);
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
{
container.setDefaultMaxSessionIdleTimeout(idleTimeout);
container.setDefaultMaxTextMessageBufferSize(maxTextMessageSize);
container.setDefaultMaxBinaryMessageBufferSize(maxBinaryMessageSize);
container.setAsyncSendTimeout(asyncSendTimeout);
container.addEndpoint(ConfigTestSocket.class);
container.addEndpoint(AnnotatedOnMessageSocket.class);
});
server.start();
client = ContainerProvider.getWebSocketContainer();
}
@AfterEach
public void stop() throws Exception
{
server.stop();
}
@ServerEndpoint("/containerDefaults")
public static class ConfigTestSocket
{
@OnOpen
public void onOpen(Session session)
{
assertThat(session.getMaxIdleTimeout(), is(idleTimeout));
assertThat(session.getMaxTextMessageBufferSize(), is(maxTextMessageSize));
assertThat(session.getMaxBinaryMessageBufferSize(), is(maxBinaryMessageSize));
assertThat(session.getAsyncRemote().getSendTimeout(), is(asyncSendTimeout));
}
}
@ServerEndpoint("/annotatedOnMessage")
public static class AnnotatedOnMessageSocket
{
@OnOpen
public void onOpen(Session session)
{
assertThat(session.getMaxTextMessageBufferSize(), is(111));
assertThat(session.getMaxBinaryMessageBufferSize(), is(maxBinaryMessageSize));
}
@OnMessage(maxMessageSize = 111)
public void onMessage(String message) throws IOException
{
}
@OnMessage()
public void onMessage(ByteBuffer message) throws IOException
{
}
}
@ParameterizedTest
@ValueSource(strings = {"/containerDefaults", "/annotatedOnMessage"})
public void testEndpointSettings(String path) throws Exception
{
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + path);
EventSocket clientEndpoint = new EventSocket();
client.connectToServer(clientEndpoint, uri);
clientEndpoint.openLatch.await(5, TimeUnit.SECONDS);
clientEndpoint.session.close(new CloseReason(CloseCodes.NORMAL_CLOSURE, "normal close"));
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
assertThat(clientEndpoint.closeReason.getCloseCode(), is(CloseCodes.NORMAL_CLOSURE));
assertThat(clientEndpoint.closeReason.getReasonPhrase(), is("normal close"));
}
}

View File

@ -160,7 +160,7 @@ public class JavaxAutobahnClient
try
{
clientContainer.connectToServer(onCaseCount, wsUri);
String msg = onCaseCount.messageQueue.poll(10, TimeUnit.SECONDS);
String msg = onCaseCount.textMessages.poll(10, TimeUnit.SECONDS);
onCaseCount.session.close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, null));
assertTrue(onCaseCount.closeLatch.await(2, TimeUnit.SECONDS));
assertNotNull(msg);

View File

@ -19,23 +19,17 @@
package org.eclipse.jetty.websocket.javax.tests.client;
import java.util.concurrent.TimeUnit;
import javax.websocket.ContainerProvider;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.exception.WebSocketWriteTimeoutException;
import org.eclipse.jetty.websocket.javax.client.JavaxWebSocketClientContainer;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketContainer;
import org.eclipse.jetty.websocket.javax.tests.EventSocket;
import org.eclipse.jetty.websocket.javax.tests.LocalServer;
import org.eclipse.jetty.websocket.javax.tests.WSEndpointTracker;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
@ -44,49 +38,48 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class WriteTimeoutTest
{
private static LocalServer server;
@ServerEndpoint("/logSocket")
public static class ServerSocket extends EventSocket
{
@Override
public void onOpen(Session session, EndpointConfig endpointConfig)
{
session.setMaxIdleTimeout(-1);
session.setMaxTextMessageBufferSize(-1);
super.onOpen(session, endpointConfig);
}
}
@BeforeAll
public static void startServer() throws Exception
private LocalServer server;
private JavaxWebSocketContainer client;
@BeforeEach
public void start() throws Exception
{
server = new LocalServer();
server.start();
server.getServerContainer().addEndpoint(LoggingSocket.class);
server.getServerContainer().addEndpoint(ServerSocket.class);
client = new JavaxWebSocketClientContainer();
client.start();
}
@AfterAll
public static void stopServer() throws Exception
@AfterEach
public void stop() throws Exception
{
client.stop();
server.stop();
}
public static class ClientEndpoint extends WSEndpointTracker implements MessageHandler.Whole<String>
{
@Override
public void onOpen(Session session, EndpointConfig config)
{
super.onOpen(session, config);
session.addMessageHandler(this);
}
@Override
public void onMessage(String message)
{
super.onWsText(message);
}
}
@Test
public void testEchoInstance() throws Exception
public void testTimeoutOnLargeMessage() throws Exception
{
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
ClientEndpoint clientEndpoint = new ClientEndpoint();
assertThat(clientEndpoint, Matchers.instanceOf(javax.websocket.Endpoint.class));
Session session = container.connectToServer(clientEndpoint, null, server.getWsUri().resolve("/logSocket"));
EventSocket clientEndpoint = new EventSocket();
Session session = client.connectToServer(clientEndpoint, server.getWsUri().resolve("/logSocket"));
session.getAsyncRemote().setSendTimeout(5);
session.setMaxTextMessageBufferSize(1024 * 1024 * 6);
session.setMaxTextMessageBufferSize(1000000);
String string = "xxxxxxx";
StringBuilder sb = new StringBuilder();
while (sb.length() < session.getMaxTextMessageBufferSize() - string.length())
@ -101,24 +94,7 @@ public class WriteTimeoutTest
}
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
assertThat(clientEndpoint.error.get(), instanceOf(WebSocketWriteTimeoutException.class));
}
@ServerEndpoint("/logSocket")
public static class LoggingSocket
{
private final Logger log = Log.getLogger(LoggingSocket.class);
@OnMessage
public void onMessage(String msg)
{
log.debug("onMessage(): {}", msg);
}
@OnError
public void onError(Throwable t)
{
log.debug("onError(): {}", t);
}
assertTrue(clientEndpoint.errorLatch.await(5, TimeUnit.SECONDS));
assertThat(clientEndpoint.error, instanceOf(WebSocketWriteTimeoutException.class));
}
}

View File

@ -142,7 +142,7 @@ public class ServerDecoderTest
EventSocket serverSocket = annotatedServerSocket.get(5, TimeUnit.SECONDS);
assertTrue(serverSocket.openLatch.await(5, TimeUnit.SECONDS));
String msg = serverSocket.messageQueue.poll(5, TimeUnit.SECONDS);
String msg = serverSocket.textMessages.poll(5, TimeUnit.SECONDS);
assertThat(msg, is("hello world="));
clientSocket.session.close();

View File

@ -108,27 +108,27 @@ public class SessionTrackingTest
Session serverSession1 = serverSessions.poll(5, TimeUnit.SECONDS);
assertNotNull(serverSession1);
sendTextFrameToAll("openSessions|in-1", session1);
assertThat(clientSocket1.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@in-1).size=1"));
assertThat(clientSocket1.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@in-1).size=1"));
Session session2 = client.connectToServer(clientSocket2, server.getWsUri().resolve("/session-info/2"));
Session serverSession2 = serverSessions.poll(5, TimeUnit.SECONDS);
assertNotNull(serverSession2);
sendTextFrameToAll("openSessions|in-2", session1, session2);
assertThat(clientSocket1.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@in-2).size=2"));
assertThat(clientSocket2.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@in-2).size=2"));
assertThat(clientSocket1.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@in-2).size=2"));
assertThat(clientSocket2.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@in-2).size=2"));
Session session3 = client.connectToServer(clientSocket3, server.getWsUri().resolve("/session-info/3"));
Session serverSession3 = serverSessions.poll(5, TimeUnit.SECONDS);
assertNotNull(serverSession3);
sendTextFrameToAll("openSessions|in-3", session1, session2, session3);
assertThat(clientSocket1.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@in-3).size=3"));
assertThat(clientSocket2.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@in-3).size=3"));
assertThat(clientSocket3.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@in-3).size=3"));
assertThat(clientSocket1.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@in-3).size=3"));
assertThat(clientSocket2.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@in-3).size=3"));
assertThat(clientSocket3.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@in-3).size=3"));
sendTextFrameToAll("openSessions|lvl-3", session1, session2, session3);
assertThat(clientSocket1.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-3).size=3"));
assertThat(clientSocket2.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-3).size=3"));
assertThat(clientSocket3.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-3).size=3"));
assertThat(clientSocket1.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-3).size=3"));
assertThat(clientSocket2.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-3).size=3"));
assertThat(clientSocket3.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-3).size=3"));
// assert session is closed, and we have received the notification from the SessionListener
session3.close();
@ -136,8 +136,8 @@ public class SessionTrackingTest
assertTrue(clientSocket3.closeLatch.await(5, TimeUnit.SECONDS));
sendTextFrameToAll("openSessions|lvl-2", session1, session2);
assertThat(clientSocket1.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-2).size=2"));
assertThat(clientSocket2.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-2).size=2"));
assertThat(clientSocket1.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-2).size=2"));
assertThat(clientSocket2.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-2).size=2"));
// assert session is closed, and we have received the notification from the SessionListener
session2.close();
@ -145,7 +145,7 @@ public class SessionTrackingTest
assertTrue(clientSocket2.closeLatch.await(5, TimeUnit.SECONDS));
sendTextFrameToAll("openSessions|lvl-1", session1);
assertThat(clientSocket1.messageQueue.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-1).size=1"));
assertThat(clientSocket1.textMessages.poll(5, TimeUnit.SECONDS), is("openSessions(@lvl-1).size=1"));
// assert session is closed, and we have received the notification from the SessionListener
session1.close();