Issue #4903 - Improved behavior for Custom ServerEndpointConfig.Configurator

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2020-06-10 11:01:30 -05:00
parent cbda92ab8c
commit 9bfc168329
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
3 changed files with 99 additions and 9 deletions

View File

@ -68,7 +68,7 @@ public final class ContainerDefaultConfigurator extends Configurator
{
// Since this is started via a ServiceLoader, this class has no Scope or context
// that can be used to obtain a ObjectFactory from.
return endpointClass.getDeclaredConstructor().newInstance();
return endpointClass.getConstructor().newInstance();
}
catch (Exception e)
{

View File

@ -121,8 +121,22 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
@Override
public void addEndpoint(Class<?> endpointClass) throws DeploymentException
{
if (endpointClass == null)
{
throw new DeploymentException("Unable to deploy null endpoint class");
}
if (isStarted() || isStarting())
{
if (!ReflectUtils.isDefaultConstructable(endpointClass))
{
throw new DeploymentException("Cannot access default constructor for the class: " + endpointClass.getName());
}
if (LOG.isDebugEnabled())
{
LOG.debug("addEndpoint({})", endpointClass);
}
ServerEndpointMetadata metadata = getServerEndpointMetadata(endpointClass, null);
addEndpoint(metadata);
}
@ -136,11 +150,8 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
}
}
private void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
private void addEndpoint(ServerEndpointMetadata metadata)
{
if (!ReflectUtils.isDefaultConstructable(metadata.getEndpointClass()))
throw new DeploymentException("Cannot access default constructor for the Endpoint class");
JsrCreator creator = new JsrCreator(this, metadata, this.configuration.getFactory().getExtensionFactory());
this.configuration.addMapping("uri-template|" + metadata.getPath(), creator);
}
@ -148,8 +159,29 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
@Override
public void addEndpoint(ServerEndpointConfig config) throws DeploymentException
{
if (config == null)
{
throw new DeploymentException("Unable to deploy null ServerEndpointConfig");
}
Class<?> endpointClass = config.getEndpointClass();
if (endpointClass == null)
{
throw new DeploymentException("Unable to deploy null endpoint class from ServerEndpointConfig: " + config.getClass().getName());
}
if (isStarted() || isStarting())
{
ServerEndpointConfig.Configurator configurator = config.getConfigurator();
// only validate constructor and class modifiers on non-custom configurators
if (configurator instanceof ContainerDefaultConfigurator)
{
if (!ReflectUtils.isDefaultConstructable(endpointClass))
{
throw new DeploymentException("Cannot access default constructor for the class: " + endpointClass.getName());
}
}
if (LOG.isDebugEnabled())
{
LOG.debug("addEndpoint({}) path={} endpoint={}", config, config.getPath(), config.getEndpointClass());

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.api.util.WSURI;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -41,6 +42,7 @@ import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class PrivateEndpointTest
@ -94,6 +96,26 @@ public class PrivateEndpointTest
}
}
public static class CustomEndpoint extends Endpoint implements MessageHandler.Whole<String>
{
public CustomEndpoint(String id)
{
// This is a valid no-default-constructor implementation, and can be added via a custom
// ServerEndpointConfig.Configurator
}
@Override
public void onOpen(Session session, EndpointConfig config)
{
session.addMessageHandler(this);
}
@Override
public void onMessage(String message)
{
}
}
@SuppressWarnings("InnerClassMayBeStatic")
public class ServerSocketNonStatic extends Endpoint implements MessageHandler.Whole<String>
{
@ -131,11 +153,47 @@ public class PrivateEndpointTest
public void testEndpoint()
{
RuntimeException error = assertThrows(RuntimeException.class, () ->
start(container -> container.addEndpoint(ServerEndpointConfig.Builder.create(ServerSocket.class, "/").build())));
{
ServerEndpointConfig config = ServerEndpointConfig.Builder.create(ServerSocket.class, "/").build();
start(container -> container.addEndpoint(config));
});
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor for the Endpoint class"));
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor"));
}
@Test
public void testCustomEndpoint() throws Exception
{
ServerEndpointConfig config = ServerEndpointConfig.Builder.create(CustomEndpoint.class, "/")
.configurator(new ServerEndpointConfig.Configurator()
{
@SuppressWarnings("unchecked")
@Override
public <T> T getEndpointInstance(Class<T> endpointClass)
{
return (T)new CustomEndpoint("server");
}
}).build();
start(container -> container.addEndpoint(config));
Session session = client.connectToServer(new CustomEndpoint("client"), WSURI.toWebsocket(server.getURI().resolve("/")));
assertNotNull(session);
}
@Test
public void testCustomEndpointNoConfigurator()
{
RuntimeException error = assertThrows(RuntimeException.class, () ->
{
ServerEndpointConfig config = ServerEndpointConfig.Builder.create(CustomEndpoint.class, "/").build();
start(container -> container.addEndpoint(config));
});
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor"));
}
@Test
@ -146,7 +204,7 @@ public class PrivateEndpointTest
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor for the Endpoint class"));
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor"));
}
@Test
@ -157,7 +215,7 @@ public class PrivateEndpointTest
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor for the Endpoint class"));
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor"));
}
@Test