Issue #207 - Support javax.websocket version 1.1

# Conflicts:
#	jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointMetadata.java
#	jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrPathParamId.java
#	jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrServerEndpointImpl.java
#	jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/JsrServerExtendsEndpointImpl.java
#	jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/SimpleServerEndpointMetadata.java
#	jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java
#	jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_GoodSignaturesTest.java
#	jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ServerAnnotatedEndpointScanner_InvalidSignaturesTest.java
This commit is contained in:
Joakim Erdfelt 2016-08-18 13:55:58 -07:00
parent df584babc0
commit 4c01fd96c8
28 changed files with 684 additions and 1171 deletions

View File

@ -73,13 +73,13 @@ import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
public class ClientContainer extends ContainerLifeCycle implements WebSocketContainer, WebSocketContainerScope
{
private static final Logger LOG = Log.getLogger(ClientContainer.class);
/** The delegated Container Scope */
private final WebSocketContainerScope scopeDelegate;
/** The jetty websocket client in use for this container */
private WebSocketClient client;
private List<Function<Object,EndpointConfig>> annotatedConfigFunctions = new ArrayList<>();
private List<Function<Object, EndpointConfig>> annotatedConfigFunctions = new ArrayList<>();
public ClientContainer()
{
@ -88,47 +88,26 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
client.setDaemon(true);
}
/**
* This is the entry point for ServerContainer, via ServletContext.getAttribute(ServerContainer.class.getName())
*
* @param scope the scope of the ServerContainer
*/
public ClientContainer(final WebSocketContainerScope scope)
public ClientContainer(WebSocketContainerScope scope)
{
boolean trustAll = Boolean.getBoolean("org.eclipse.jetty.websocket.jsr356.ssl-trust-all");
this.scopeDelegate = scope;
client = new WebSocketClient(scope, new SslContextFactory(trustAll));
client.setSessionFactory(new JsrSessionFactory(this));
addBean(client);
// annotatedConfigFunctions.add(new ClientEndpointConfigFunction());
// annotatedConfigFunctions.add(new ClientEndpointConfigFunction());
ShutdownThread.register(this);
}
private Session connect(ConfiguredEndpoint instance, URI path) throws IOException
{
synchronized (this.client)
{
if (this.internalClient && !this.client.isStarted())
{
try
{
this.client.start();
addManaged(this.client);
}
catch (Exception e)
{
throw new RuntimeException("Unable to start Client", e);
}
}
}
Objects.requireNonNull(instance, "EndpointInstance cannot be null");
Objects.requireNonNull(path, "Path cannot be null");
Objects.requireNonNull(instance,"EndpointInstance cannot be null");
Objects.requireNonNull(path,"Path cannot be null");
ClientEndpointConfig config = (ClientEndpointConfig)instance.getConfig();
ClientEndpointConfig config = (ClientEndpointConfig) instance.getConfig();
ClientUpgradeRequest req = new ClientUpgradeRequest();
UpgradeListener upgradeListener = null;
@ -136,191 +115,204 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
{
req.addExtensions(new JsrExtensionConfig(ext));
}
if (config.getPreferredSubprotocols().size() > 0)
{
req.setSubProtocols(config.getPreferredSubprotocols());
}
if (config.getConfigurator() != null)
{
upgradeListener = new JsrUpgradeListener(config.getConfigurator());
}
Future<org.eclipse.jetty.websocket.api.Session> futSess = client.connect(instance,path,req,upgradeListener);
Future<org.eclipse.jetty.websocket.api.Session> futSess = client.connect(instance, path, req, upgradeListener);
try
{
return (JsrSession)futSess.get();
return (JsrSession) futSess.get();
}
catch (InterruptedException e)
{
throw new IOException("Connect failure",e);
throw new IOException("Connect failure", e);
}
catch (ExecutionException e)
{
// Unwrap Actual Cause
Throwable cause = e.getCause();
if (cause instanceof IOException)
{
// Just rethrow
throw (IOException)cause;
throw (IOException) cause;
}
else
{
throw new IOException("Connect failure",cause);
throw new IOException("Connect failure", cause);
}
}
}
@Override
public Session connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig config, URI path) throws DeploymentException, IOException
{
ConfiguredEndpoint instance = newClientEndpointInstance(endpointClass,config);
return connect(instance,path);
ConfiguredEndpoint instance = newConfiguredEndpoint(endpointClass, config);
return connect(instance, path);
}
@Override
public Session connectToServer(Class<?> annotatedEndpointClass, URI path) throws DeploymentException, IOException
{
ConfiguredEndpoint instance = newClientEndpointInstance(annotatedEndpointClass,null);
return connect(instance,path);
ConfiguredEndpoint instance = newConfiguredEndpoint(annotatedEndpointClass, null);
return connect(instance, path);
}
@Override
public Session connectToServer(Endpoint endpoint, ClientEndpointConfig config, URI path) throws DeploymentException, IOException
{
ConfiguredEndpoint instance = newClientEndpointInstance(endpoint,config);
return connect(instance,path);
ConfiguredEndpoint instance = newConfiguredEndpoint(endpoint, config);
return connect(instance, path);
}
@Override
public Session connectToServer(Object endpoint, URI path) throws DeploymentException, IOException
{
ConfiguredEndpoint instance = newClientEndpointInstance(endpoint,null);
return connect(instance,path);
ConfiguredEndpoint instance = newConfiguredEndpoint(endpoint, null);
return connect(instance, path);
}
@Override
protected void doStop() throws Exception
{
ShutdownThread.deregister(this);
super.doStop();
}
@Override
public ByteBufferPool getBufferPool()
{
return scopeDelegate.getBufferPool();
}
public WebSocketClient getClient()
{
return client;
}
@Override
public long getDefaultAsyncSendTimeout()
{
return client.getAsyncWriteTimeout();
}
@Override
public int getDefaultMaxBinaryMessageBufferSize()
{
return client.getMaxBinaryMessageBufferSize();
}
@Override
public long getDefaultMaxSessionIdleTimeout()
{
return client.getMaxIdleTimeout();
}
@Override
public int getDefaultMaxTextMessageBufferSize()
{
return client.getMaxTextMessageBufferSize();
}
@Override
public Executor getExecutor()
{
return scopeDelegate.getExecutor();
}
@Override
public Set<Extension> getInstalledExtensions()
{
Set<Extension> ret = new HashSet<>();
ExtensionFactory extensions = client.getExtensionFactory();
for (String name : extensions.getExtensionNames())
{
ret.add(new JsrExtension(name));
}
return ret;
}
@Override
public DecoratedObjectFactory getObjectFactory()
{
return scopeDelegate.getObjectFactory();
}
/**
* Used in {@link Session#getOpenSessions()}
*
* @return the set of open sessions
*/
public Set<Session> getOpenSessions()
{
return new HashSet<>(getBeans(Session.class));
}
@Override
public WebSocketPolicy getPolicy()
{
return scopeDelegate.getPolicy();
}
@Override
public SslContextFactory getSslContextFactory()
{
return scopeDelegate.getSslContextFactory();
}
private ConfiguredEndpoint newClientEndpointInstance(Class<?> endpointClass, ClientEndpointConfig config)
private ConfiguredEndpoint newConfiguredEndpoint(Class<?> endpointClass, EndpointConfig config)
{
try
{
return newClientEndpointInstance(endpointClass.newInstance(),config);
return newConfiguredEndpoint(endpointClass.newInstance(), config);
}
catch (InstantiationException | IllegalAccessException e)
catch (DeploymentException | InstantiationException | IllegalAccessException e)
{
throw new InvalidWebSocketException("Unable to instantiate websocket: " + endpointClass.getClass());
}
}
public ConfiguredEndpoint newClientEndpointInstance(Object endpoint, ClientEndpointConfig config)
public ConfiguredEndpoint newConfiguredEndpoint(Object endpoint, EndpointConfig providedConfig) throws DeploymentException
{
ClientEndpointConfig cec = config;
EndpointConfig config = providedConfig;
if (config == null)
{
// Get Config from Annotation
ClientEndpoint anno = endpoint.getClass().getAnnotation(ClientEndpoint.class);
if(anno != null)
{
cec = new AnnotatedClientEndpointConfig(anno);
}
else
{
cec = new EmptyClientEndpointConfig();
}
config = newEmptyConfig(endpoint);
}
return new ConfiguredEndpoint(endpoint,cec);
config = readAnnotatedConfig(endpoint, config);
return new ConfiguredEndpoint(endpoint, config);
}
protected EndpointConfig newEmptyConfig(Object endpoint)
{
return new EmptyClientEndpointConfig();
}
protected EndpointConfig readAnnotatedConfig(Object endpoint, EndpointConfig config) throws DeploymentException
{
ClientEndpoint anno = endpoint.getClass().getAnnotation(ClientEndpoint.class);
if (anno != null)
{
// Overwrite Config from Annotation
// TODO: should we merge with provided config?
return new AnnotatedClientEndpointConfig(anno);
}
return config;
}
@Override
public void onSessionClosed(WebSocketSession session)
{
@ -330,11 +322,11 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
}
else
{
LOG.warn("JSR356 Implementation should not be mixed with native implementation: Expected {} to implement {}",session.getClass().getName(),
LOG.warn("JSR356 Implementation should not be mixed with native implementation: Expected {} to implement {}", session.getClass().getName(),
Session.class.getName());
}
}
@Override
public void onSessionOpened(WebSocketSession session)
{
@ -344,17 +336,17 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
}
else
{
LOG.warn("JSR356 Implementation should not be mixed with Jetty native websocket implementation: Expected {} to implement {}",session.getClass().getName(),
LOG.warn("JSR356 Implementation should not be mixed with Jetty native websocket implementation: Expected {} to implement {}", session.getClass().getName(),
Session.class.getName());
}
}
@Override
public void setAsyncSendTimeout(long ms)
{
client.setAsyncWriteTimeout(ms);
}
@Override
public void setDefaultMaxBinaryMessageBufferSize(int max)
{
@ -363,13 +355,13 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
// incoming streaming buffer size
client.setMaxBinaryMessageBufferSize(max);
}
@Override
public void setDefaultMaxSessionIdleTimeout(long ms)
{
client.setMaxIdleTimeout(ms);
}
@Override
public void setDefaultMaxTextMessageBufferSize(int max)
{

View File

@ -83,6 +83,12 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
this.availableDecoders = new AvailableDecoders(this.config);
this.availableEncoders = new AvailableEncoders(this.config);
if(this.config instanceof PathParamProvider)
{
PathParamProvider pathParamProvider = (PathParamProvider) this.config;
pathParameters.putAll(pathParamProvider.getPathParams());
}
this.id = id;
}

View File

@ -0,0 +1,34 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356;
import java.util.Map;
/**
* Optional interface for custom {@link javax.websocket.EndpointConfig} implementations
* in Jetty to expose Path Param values used during the {@link org.eclipse.jetty.websocket.jsr356.function.JsrEndpointFunctions}
* resolution of methods.
* <p>
* Mostly a feature of the JSR356 Server implementation and its <code>&#064;javax.websocket.server.PathParam</code> annotation.
* </p>
*/
public interface PathParamProvider
{
Map<String,String> getPathParams();
}

View File

@ -28,6 +28,7 @@ import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
@ -42,25 +43,24 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
private final List<Class<? extends Encoder>> encoders;
private final ServerEndpointConfig.Configurator configurator;
private final List<String> subprotocols;
private Map<String, Object> userProperties;
private List<Extension> extensions;
public AnnotatedServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
{
this(containerScope,endpointClass,anno,null);
this(containerScope, endpointClass, anno, null);
}
public AnnotatedServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, ServerEndpoint anno, ServerEndpointConfig baseConfig) throws DeploymentException
public AnnotatedServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, ServerEndpoint anno, EndpointConfig baseConfig) throws DeploymentException
{
ServerEndpointConfig.Configurator configr = null;
// Copy from base config
if (baseConfig != null)
ServerEndpointConfig baseServerConfig = null;
if (baseConfig instanceof ServerEndpointConfig)
{
configr = baseConfig.getConfigurator();
baseServerConfig = (ServerEndpointConfig) baseConfig;
}
// Decoders (favor provided config over annotation)
if (baseConfig != null && baseConfig.getDecoders() != null && baseConfig.getDecoders().size() > 0)
{
@ -70,7 +70,7 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
{
this.decoders = Collections.unmodifiableList(Arrays.asList(anno.decoders()));
}
// AvailableEncoders (favor provided config over annotation)
if (baseConfig != null && baseConfig.getEncoders() != null && baseConfig.getEncoders().size() > 0)
{
@ -80,27 +80,27 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
{
this.encoders = Collections.unmodifiableList(Arrays.asList(anno.encoders()));
}
// Sub Protocols (favor provided config over annotation)
if (baseConfig != null && baseConfig.getSubprotocols() != null && baseConfig.getSubprotocols().size() > 0)
if (baseServerConfig != null && baseServerConfig.getSubprotocols() != null && baseServerConfig.getSubprotocols().size() > 0)
{
this.subprotocols = Collections.unmodifiableList(baseConfig.getSubprotocols());
this.subprotocols = Collections.unmodifiableList(baseServerConfig.getSubprotocols());
}
else
{
this.subprotocols = Collections.unmodifiableList(Arrays.asList(anno.subprotocols()));
}
// Path (favor provided config over annotation)
if (baseConfig != null && baseConfig.getPath() != null && baseConfig.getPath().length() > 0)
if (baseServerConfig != null && baseServerConfig.getPath() != null && baseServerConfig.getPath().length() > 0)
{
this.path = baseConfig.getPath();
this.path = baseServerConfig.getPath();
}
else
{
this.path = anno.value();
}
// supplied by init lifecycle
this.extensions = new ArrayList<>();
// always what is passed in
@ -112,88 +112,101 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
userProperties.putAll(baseConfig.getUserProperties());
}
ServerEndpointConfig.Configurator cfgr;
if (anno.configurator() == ServerEndpointConfig.Configurator.class)
ServerEndpointConfig.Configurator rawConfigurator = getConfigurator(baseServerConfig, anno);
// Make sure all Configurators obtained are decorated
this.configurator = containerScope.getObjectFactory().decorate(rawConfigurator);
}
private Configurator getConfigurator(ServerEndpointConfig baseServerConfig, ServerEndpoint anno) throws DeploymentException
{
Configurator ret = null;
// Copy from base config
if (baseServerConfig != null)
{
if (configr != null)
{
cfgr = configr;
}
else
{
cfgr = new ContainerDefaultConfigurator();
}
ret = baseServerConfig.getConfigurator();
}
else
if (anno != null)
{
// Is this using the JSR356 spec/api default?
if (anno.configurator() == ServerEndpointConfig.Configurator.class)
{
// Return the spec default impl if one wasn't provided as part of the base config
if (ret == null)
return new ContainerDefaultConfigurator();
else
return ret;
}
// Instantiate the provided configurator
try
{
cfgr = anno.configurator().newInstance();
return anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ClientEndpoint.configurator() of ");
err.append("Unable to instantiate ServerEndpoint.configurator() of ");
err.append(anno.configurator().getName());
err.append(" defined as annotation in ");
err.append(anno.getClass().getName());
throw new DeploymentException(err.toString(),e);
throw new DeploymentException(err.toString(), e);
}
}
// Make sure all Configurators obtained are decorated
this.configurator = containerScope.getObjectFactory().decorate(cfgr);
return ret;
}
@Override
public ServerEndpointConfig.Configurator getConfigurator()
{
return configurator;
}
@Override
public List<Class<? extends Decoder>> getDecoders()
{
return decoders;
}
@Override
public List<Class<? extends Encoder>> getEncoders()
{
return encoders;
}
@Override
public Class<?> getEndpointClass()
{
return endpointClass;
}
@Override
public List<Extension> getExtensions()
{
return extensions;
}
@Override
public String getPath()
{
return path;
}
@Override
public List<String> getSubprotocols()
{
return subprotocols;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
@Override
public String toString()
{

View File

@ -1,109 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.LinkedList;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
public class AnnotatedServerEndpointMetadata extends AnnotatedEndpointMetadata<ServerEndpoint,ServerEndpointConfig> implements ServerEndpointMetadata
{
private final ServerEndpoint endpoint;
private final AnnotatedServerEndpointConfig config;
protected AnnotatedServerEndpointMetadata(WebSocketContainerScope containerScope, Class<?> websocket, ServerEndpointConfig baseConfig) throws DeploymentException
{
super(websocket);
ServerEndpoint anno = websocket.getAnnotation(ServerEndpoint.class);
if (anno == null)
{
throw new InvalidWebSocketException("Unsupported WebSocket object, missing @" + ServerEndpoint.class + " annotation");
}
this.endpoint = anno;
this.config = new AnnotatedServerEndpointConfig(containerScope,websocket,anno,baseConfig);
getDecoders().addAll(anno.decoders());
getEncoders().addAll(anno.encoders());
}
@Override
public void customizeParamsOnClose(LinkedList<IJsrParamId> params)
{
super.customizeParamsOnClose(params);
params.addFirst(JsrPathParamId.INSTANCE);
}
@Override
public void customizeParamsOnError(LinkedList<IJsrParamId> params)
{
super.customizeParamsOnError(params);
params.addFirst(JsrPathParamId.INSTANCE);
}
@Override
public void customizeParamsOnOpen(LinkedList<IJsrParamId> params)
{
super.customizeParamsOnOpen(params);
params.addFirst(JsrPathParamId.INSTANCE);
}
@Override
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
{
super.customizeParamsOnMessage(params);
params.addFirst(JsrPathParamId.INSTANCE);
}
@Override
public ServerEndpoint getAnnotation()
{
return endpoint;
}
public AnnotatedServerEndpointConfig getConfig()
{
return config;
}
public String getPath()
{
return config.getPath();
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("AnnotatedServerEndpointMetadata[endpoint=");
builder.append(endpoint);
builder.append(",config=");
builder.append(config);
builder.append("]");
return builder.toString();
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -36,7 +37,6 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrExtension;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
@ -49,13 +49,13 @@ public class JsrCreator implements WebSocketCreator
public static final String PROP_LOCALES = "javax.websocket.upgrade.locales";
private static final Logger LOG = Log.getLogger(JsrCreator.class);
private final WebSocketContainerScope containerScope;
private final ServerEndpointMetadata metadata;
private final ServerEndpointConfig baseConfig;
private final ExtensionFactory extensionFactory;
public JsrCreator(WebSocketContainerScope containerScope, ServerEndpointMetadata metadata, ExtensionFactory extensionFactory)
public JsrCreator(WebSocketContainerScope containerScope, ServerEndpointConfig config, ExtensionFactory extensionFactory)
{
this.containerScope = containerScope;
this.metadata = metadata;
this.baseConfig = config;
this.extensionFactory = extensionFactory;
}
@ -65,12 +65,18 @@ public class JsrCreator implements WebSocketCreator
JsrHandshakeRequest jsrHandshakeRequest = new JsrHandshakeRequest(req);
JsrHandshakeResponse jsrHandshakeResponse = new JsrHandshakeResponse(resp);
// Get raw config, as defined when the endpoint was added to the container
ServerEndpointConfig config = metadata.getConfig();
// Establish a copy of the config, so that the UserProperties are unique
// per upgrade request.
config = new BasicServerEndpointConfig(containerScope, config);
ServerEndpointConfig config = new ServerEndpointConfigWrapper(baseConfig)
{
Map<String,Object> userProperties = new HashMap<>(baseConfig.getUserProperties());
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
};
// Bug 444617 - Expose localAddress and remoteAddress for jsr modify handshake to use
// This is being implemented as an optional set of userProperties so that
@ -143,7 +149,7 @@ public class JsrCreator implements WebSocketCreator
UriTemplatePathSpec wspathSpec = (UriTemplatePathSpec)pathSpec;
String requestPath = req.getRequestPath();
// Wrap the config with the path spec information
config = new PathParamServerEndpointConfig(containerScope,config,wspathSpec,requestPath);
config = new PathParamServerEndpointConfig(config,wspathSpec,requestPath);
}
// [JSR] Step 5: Call modifyHandshake
@ -156,16 +162,7 @@ public class JsrCreator implements WebSocketCreator
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.
PathSpec pathSpec = hsreq.getRequestPathSpec();
if (pathSpec instanceof UriTemplatePathSpec)
{
// We have a PathParam path spec
UriTemplatePathSpec wspathSpec = (UriTemplatePathSpec)pathSpec;
String requestPath = req.getRequestPath();
// Wrap the config with the path spec information
config = new PathParamServerEndpointConfig(containerScope,config,wspathSpec,requestPath);
}
return new ConfiguredEndpoint(endpoint,config);
return endpoint;
}
catch (InstantiationException e)
{
@ -178,6 +175,6 @@ public class JsrCreator implements WebSocketCreator
@Override
public String toString()
{
return String.format("%s[metadata=%s]",this.getClass().getName(),metadata);
return String.format("%s[config=%s]",this.getClass().getName(),baseConfig);
}
}

View File

@ -1,49 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.server.PathParam;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
import org.eclipse.jetty.websocket.jsr356.annotations.Param;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
* Param handling for static parameters annotated with &#064;{@link PathParam}
*/
public class JsrPathParamId implements IJsrParamId
{
public static final IJsrParamId INSTANCE = new JsrPathParamId();
@Override
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
{
PathParam pathparam = param.getAnnotation(PathParam.class);
if(pathparam != null)
{
param.bind(Role.PATH_PARAM);
param.setPathParamName(pathparam.value());
return true;
}
return false;
}
}

View File

@ -1,110 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
import org.eclipse.jetty.websocket.jsr356.annotations.OnMessageCallable;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
* Event Driver for classes annotated with &#064;{@link ServerEndpoint}
*/
public class JsrServerEndpointImpl implements EventDriverImpl
{
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy) throws Throwable
{
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
AnnotatedServerEndpointMetadata metadata = (AnnotatedServerEndpointMetadata)ei.getMetadata();
JsrEvents<ServerEndpoint, ServerEndpointConfig> events = new JsrEvents<>(metadata);
// Handle @OnMessage maxMessageSizes
int maxBinaryMessage = getMaxMessageSize(policy.getMaxBinaryMessageSize(),metadata.onBinary,metadata.onBinaryStream);
int maxTextMessage = getMaxMessageSize(policy.getMaxTextMessageSize(),metadata.onText,metadata.onTextStream);
policy.setMaxBinaryMessageSize(maxBinaryMessage);
policy.setMaxTextMessageSize(maxTextMessage);
JsrAnnotatedEventDriver driver = new JsrAnnotatedEventDriver(policy,ei,events);
// Handle @PathParam values
ServerEndpointConfig config = (ServerEndpointConfig)ei.getConfig();
if (config instanceof PathParamServerEndpointConfig)
{
PathParamServerEndpointConfig ppconfig = (PathParamServerEndpointConfig)config;
driver.setPathParameters(ppconfig.getPathParamMap());
}
return driver;
}
@Override
public String describeRule()
{
return "class is annotated with @" + ServerEndpoint.class.getName();
}
private int getMaxMessageSize(int defaultMaxMessageSize, OnMessageCallable... onMessages)
{
for (OnMessageCallable callable : onMessages)
{
if (callable == null)
{
continue;
}
OnMessage onMsg = callable.getMethod().getAnnotation(OnMessage.class);
if (onMsg == null)
{
continue;
}
if (onMsg.maxMessageSize() > 0)
{
return (int)onMsg.maxMessageSize();
}
}
return defaultMaxMessageSize;
}
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
ServerEndpoint anno = endpoint.getClass().getAnnotation(ServerEndpoint.class);
return (anno != null);
}
}

View File

@ -1,71 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
public class JsrServerExtendsEndpointImpl implements EventDriverImpl
{
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
JsrEndpointEventDriver driver = new JsrEndpointEventDriver(policy, ei);
ServerEndpointConfig config = (ServerEndpointConfig)ei.getConfig();
if (config instanceof PathParamServerEndpointConfig)
{
PathParamServerEndpointConfig ppconfig = (PathParamServerEndpointConfig)config;
driver.setPathParameters(ppconfig.getPathParamMap());
}
return driver;
}
@Override
public String describeRule()
{
return "class extends " + javax.websocket.Endpoint.class.getName();
}
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
return (endpoint instanceof javax.websocket.Endpoint);
}
}

View File

@ -18,13 +18,23 @@
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.PathParam;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.common.reflect.Arg;
import org.eclipse.jetty.websocket.common.reflect.ArgIdentifier;
public interface ServerEndpointMetadata extends EndpointMetadata
/**
* Identify method parameters tagged with &#064;{@link javax.websocket.server.PathParam}
*/
@SuppressWarnings("unused")
public class PathParamArgIdentifier implements ArgIdentifier
{
ServerEndpointConfig getConfig();
public String getPath();
@Override
public Arg apply(Arg arg)
{
PathParam param = arg.getAnnotation(PathParam.class);
if (param != null)
arg.setTag(param.value());
return arg;
}
}

View File

@ -24,20 +24,20 @@ import java.util.Map;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.PathParamProvider;
/**
* Wrapper for a {@link ServerEndpointConfig} where there is PathParam information from the incoming request.
* Make {@link javax.websocket.server.PathParam} information from the incoming request available
* on {@link ServerEndpointConfig}
*/
public class PathParamServerEndpointConfig extends BasicServerEndpointConfig implements ServerEndpointConfig
public class PathParamServerEndpointConfig extends ServerEndpointConfigWrapper implements PathParamProvider
{
private final Map<String, String> pathParamMap;
public PathParamServerEndpointConfig(WebSocketContainerScope containerScope, ServerEndpointConfig config, UriTemplatePathSpec pathSpec, String requestPath)
public PathParamServerEndpointConfig(ServerEndpointConfig config, UriTemplatePathSpec pathSpec, String requestPath)
{
super(containerScope, config);
super(config);
Map<String, String> pathMap = pathSpec.getPathParams(requestPath);
pathParamMap = new HashMap<>();
if (pathMap != null)
@ -45,8 +45,9 @@ public class PathParamServerEndpointConfig extends BasicServerEndpointConfig imp
pathParamMap.putAll(pathMap);
}
}
public Map<String, String> getPathParamMap()
@Override
public Map<String, String> getPathParams()
{
return pathParamMap;
}

View File

@ -25,7 +25,7 @@ import java.util.Set;
import java.util.concurrent.Executor;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import javax.websocket.server.ServerEndpoint;
@ -38,13 +38,9 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrSessionFactory;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.server.NativeWebSocketConfiguration;
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
@ManagedObject("JSR356 Server Container")
@ -52,122 +48,121 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
{
private static final Logger LOG = Log.getLogger(ServerContainer.class);
/**
* Get the WebSocketContainer out of the current ThreadLocal reference
* of the active ContextHandler.
*
* @return the WebSocketContainer if found, null if not found.
*/
public static WebSocketContainer getWebSocketContainer()
{
ContextHandler.Context context = ContextHandler.getCurrentContext();
if (context == null)
return null;
ContextHandler handler = ContextHandler.getContextHandler(context);
if (handler == null)
return null;
if (!(handler instanceof ServletContextHandler))
return null;
return (javax.websocket.WebSocketContainer) handler.getServletContext().getAttribute("javax.websocket.server.ServerContainer");
}
private final NativeWebSocketConfiguration configuration;
private final MappedWebSocketCreator mappedCreator;
private final WebSocketServerFactory webSocketServerFactory;
private List<Class<?>> deferredEndpointClasses;
private List<ServerEndpointConfig> deferredEndpointConfigs;
/**
* @deprecated use {@code ServerContainer(NativeWebSocketConfiguration, HttpClient)} instead
*/
@Deprecated
public ServerContainer(NativeWebSocketConfiguration configuration, Executor executor)
public ServerContainer(MappedWebSocketCreator creator, WebSocketServerFactory factory, Executor executor)
{
this(configuration, (HttpClient) null);
super(factory);
this.mappedCreator = creator;
this.webSocketServerFactory = factory;
this.webSocketServerFactory.addSessionFactory(new JsrSessionFactory(this));
addBean(webSocketServerFactory);
}
public ServerContainer(NativeWebSocketConfiguration configuration, HttpClient httpClient)
@Override
protected EndpointConfig newEmptyConfig(Object endpoint)
{
super(configuration.getFactory(), httpClient);
this.configuration = configuration;
EventDriverFactory eventDriverFactory = this.configuration.getFactory().getEventDriverFactory();
eventDriverFactory.addImplementation(new JsrServerEndpointImpl());
eventDriverFactory.addImplementation(new JsrServerExtendsEndpointImpl());
this.configuration.getFactory().addSessionFactory(new JsrSessionFactory(this));
addBean(this.configuration);
return new UndefinedServerEndpointConfig(endpoint.getClass());
}
public ConfiguredEndpoint newClientEndpointInstance(Object endpoint, ServerEndpointConfig config, String path)
protected EndpointConfig readAnnotatedConfig(Object endpoint, EndpointConfig config) throws DeploymentException
{
EndpointMetadata metadata = getClientEndpointMetadata(endpoint.getClass(),config);
ServerEndpointConfig cec = config;
if (config == null)
ServerEndpoint anno = endpoint.getClass().getAnnotation(ServerEndpoint.class);
if (anno != null)
{
if (metadata instanceof AnnotatedServerEndpointMetadata)
{
cec = ((AnnotatedServerEndpointMetadata)metadata).getConfig();
}
else
{
cec = new BasicServerEndpointConfig(this,endpoint.getClass(),path);
}
// Overwrite Config from Annotation
// TODO: should we merge with provided config?
return new AnnotatedServerEndpointConfig(this, endpoint.getClass(), anno, config);
}
return new ConfiguredEndpoint(endpoint,cec,metadata);
return config;
}
/**
* Register a &#064;{@link ServerEndpoint} annotated endpoint class to
* the server
*
* @param endpointClass the annotated endpoint class to add to the server
* @throws DeploymentException if unable to deploy that endpoint class
* @see javax.websocket.server.ServerContainer#addEndpoint(Class)
*/
@Override
public void addEndpoint(Class<?> endpointClass) throws DeploymentException
{
if (endpointClass == null)
{
throw new DeploymentException("EndpointClass is null");
}
if (isStarted() || isStarting())
{
ServerEndpointMetadata metadata = getServerEndpointMetadata(endpointClass,null);
addEndpoint(metadata);
ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class);
if (anno == null)
{
throw new DeploymentException(String.format("Class must be @%s annotated: %s",
ServerEndpoint.class.getName(), endpointClass.getName()));
}
ServerEndpointConfig config = new AnnotatedServerEndpointConfig(this, endpointClass, anno);
addEndpointMapping(config);
}
else
{
if (deferredEndpointClasses == null)
{
deferredEndpointClasses = new ArrayList<Class<?>>();
deferredEndpointClasses = new ArrayList<>();
}
deferredEndpointClasses.add(endpointClass);
}
}
private void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
{
JsrCreator creator = new JsrCreator(this,metadata,this.configuration.getFactory().getExtensionFactory());
this.configuration.addMapping("uri-template|" + metadata.getPath(), creator);
}
/**
* Register a ServerEndpointConfig to the server
*
* @param config the endpoint config to add
* @throws DeploymentException if unable to deploy that endpoint class
* @see javax.websocket.server.ServerContainer#addEndpoint(ServerEndpointConfig)
*/
@Override
public void addEndpoint(ServerEndpointConfig config) throws DeploymentException
{
if (config == null)
{
throw new DeploymentException("ServerEndpointConfig is null");
}
if (isStarted() || isStarting())
{
if (LOG.isDebugEnabled())
{
LOG.debug("addEndpoint({}) path={} endpoint={}",config,config.getPath(),config.getEndpointClass());
LOG.debug("addEndpoint({}) path={} endpoint={}", config, config.getPath(), config.getEndpointClass());
}
ServerEndpointMetadata metadata = getServerEndpointMetadata(config.getEndpointClass(),config);
addEndpoint(metadata);
addEndpointMapping(config);
}
else
{
if (deferredEndpointConfigs == null)
{
deferredEndpointConfigs = new ArrayList<ServerEndpointConfig>();
deferredEndpointConfigs = new ArrayList<>();
}
deferredEndpointConfigs.add(config);
}
}
private void addEndpointMapping(ServerEndpointConfig config) throws DeploymentException
{
JsrCreator creator = new JsrCreator(this, config, webSocketServerFactory.getExtensionFactory());
mappedCreator.addMapping(new UriTemplatePathSpec(config.getPath()), creator);
}
@Override
protected void doStart() throws Exception
{
// Proceed with Normal Startup
super.doStart();
// Process Deferred Endpoints
if (deferredEndpointClasses != null)
{
@ -177,7 +172,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
}
deferredEndpointClasses.clear();
}
if (deferredEndpointConfigs != null)
{
for (ServerEndpointConfig config : deferredEndpointConfigs)
@ -187,58 +182,25 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
deferredEndpointConfigs.clear();
}
}
public ServerEndpointMetadata getServerEndpointMetadata(final Class<?> endpoint, final ServerEndpointConfig config) throws DeploymentException
{
ServerEndpointMetadata metadata = null;
ServerEndpoint anno = endpoint.getAnnotation(ServerEndpoint.class);
if (anno != null)
{
// Annotated takes precedence here
AnnotatedServerEndpointMetadata ametadata = new AnnotatedServerEndpointMetadata(this,endpoint,config);
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(ametadata);
metadata = ametadata;
scanner.scan();
}
else if (Endpoint.class.isAssignableFrom(endpoint))
{
// extends Endpoint
@SuppressWarnings("unchecked")
Class<? extends Endpoint> eendpoint = (Class<? extends Endpoint>)endpoint;
metadata = new SimpleServerEndpointMetadata(eendpoint,config);
}
else
{
StringBuilder err = new StringBuilder();
err.append("Not a recognized websocket [");
err.append(endpoint.getName());
err.append("] does not extend @").append(ServerEndpoint.class.getName());
err.append(" or extend from ").append(Endpoint.class.getName());
throw new DeploymentException("Unable to identify as valid Endpoint: " + endpoint);
}
return metadata;
}
@Override
public long getDefaultAsyncSendTimeout()
{
return this.configuration.getPolicy().getAsyncWriteTimeout();
}
@Override
public int getDefaultMaxBinaryMessageBufferSize()
{
return this.configuration.getPolicy().getMaxBinaryMessageSize();
}
@Override
public long getDefaultMaxSessionIdleTimeout()
{
return this.configuration.getPolicy().getIdleTimeout();
}
@Override
public int getDefaultMaxTextMessageBufferSize()
{
@ -249,14 +211,14 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
{
return this.configuration.getFactory();
}
@Override
public void setAsyncSendTimeout(long ms)
{
super.setAsyncSendTimeout(ms);
this.configuration.getPolicy().setAsyncWriteTimeout(ms);
}
@Override
public void setDefaultMaxBinaryMessageBufferSize(int max)
{
@ -266,14 +228,14 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
// incoming streaming buffer size
this.configuration.getPolicy().setMaxBinaryMessageBufferSize(max);
}
@Override
public void setDefaultMaxSessionIdleTimeout(long ms)
{
super.setDefaultMaxSessionIdleTimeout(ms);
this.configuration.getPolicy().setIdleTimeout(ms);
}
@Override
public void setDefaultMaxTextMessageBufferSize(int max)
{
@ -283,19 +245,19 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
// incoming streaming buffer size
this.configuration.getPolicy().setMaxTextMessageBufferSize(max);
}
@Override
public void onSessionClosed(WebSocketSession session)
{
getWebSocketServerFactory().onSessionClosed(session);
}
@Override
public void onSessionOpened(WebSocketSession session)
{
getWebSocketServerFactory().onSessionOpened(session);
}
@Override
public Set<Session> getOpenSessions()
{

View File

@ -0,0 +1,85 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.List;
import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpointConfig;
public abstract class ServerEndpointConfigWrapper implements ServerEndpointConfig
{
private final ServerEndpointConfig delegate;
public ServerEndpointConfigWrapper(ServerEndpointConfig delegate)
{
this.delegate = delegate;
}
@Override
public List<Class<? extends Encoder>> getEncoders()
{
return delegate.getEncoders();
}
@Override
public List<Class<? extends Decoder>> getDecoders()
{
return delegate.getDecoders();
}
@Override
public Map<String, Object> getUserProperties()
{
return delegate.getUserProperties();
}
@Override
public Class<?> getEndpointClass()
{
return delegate.getEndpointClass();
}
@Override
public String getPath()
{
return delegate.getPath();
}
@Override
public List<String> getSubprotocols()
{
return delegate.getSubprotocols();
}
@Override
public List<Extension> getExtensions()
{
return delegate.getExtensions();
}
@Override
public Configurator getConfigurator()
{
return delegate.getConfigurator();
}
}

View File

@ -1,61 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.Endpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.client.SimpleEndpointMetadata;
public class SimpleServerEndpointMetadata extends SimpleEndpointMetadata implements ServerEndpointMetadata
{
private final ServerEndpointConfig config;
public SimpleServerEndpointMetadata(Class<? extends Endpoint> endpointClass, ServerEndpointConfig config)
{
super(endpointClass,config);
this.config = config;
}
@Override
public ServerEndpointConfig getConfig()
{
return config;
}
@Override
public String getPath()
{
return config.getPath();
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("SimpleServerEndpointMetadata [");
builder.append("config=").append(config.getClass().getName());
builder.append(",path=").append(config.getPath());
builder.append(",endpoint=").append(config.getEndpointClass());
builder.append(",decoders=").append(config.getDecoders());
builder.append(",encoders=").append(config.getEncoders());
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,100 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpointConfig;
public class UndefinedServerEndpointConfig implements ServerEndpointConfig
{
private final List<Class<? extends Decoder>> decoders;
private final List<Class<? extends Encoder>> encoders;
private final List<Extension> extensions;
private final List<String> subprotocols;
private final ServerEndpointConfig.Configurator configurator;
private final Class<?> endpointClass;
private Map<String, Object> userProperties;
public UndefinedServerEndpointConfig(Class<?> endpointClass)
{
this.endpointClass = endpointClass;
this.decoders = new ArrayList<>();
this.encoders = new ArrayList<>();
this.subprotocols = new ArrayList<>();
this.extensions = new ArrayList<>();
this.userProperties = new HashMap<>();
this.configurator = new ContainerDefaultConfigurator();
}
@Override
public List<Class<? extends Encoder>> getEncoders()
{
return encoders;
}
@Override
public List<Class<? extends Decoder>> getDecoders()
{
return decoders;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
@Override
public Class<?> getEndpointClass()
{
return endpointClass;
}
@Override
public String getPath()
{
throw new RuntimeException("Using an UndefinedServerEndpointConfig");
}
@Override
public List<String> getSubprotocols()
{
return subprotocols;
}
@Override
public List<Extension> getExtensions()
{
return extensions;
}
@Override
public ServerEndpointConfig.Configurator getConfigurator()
{
return configurator;
}
}

View File

@ -0,0 +1 @@
org.eclipse.jetty.websocket.common.reflect.NameArgIdentifier

View File

@ -20,8 +20,8 @@ package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.containsString;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@ -55,7 +55,7 @@ public class AnnotatedServerEndpointTest
@BeforeClass
public static void startServer() throws Exception
{
File testdir = MavenTestingUtils.getTargetTestingDir(AnnotatedServerEndpointTest.class.getName());
Path testdir = MavenTestingUtils.getTargetTestingPath(AnnotatedServerEndpointTest.class.getName());
server = new WSServer(testdir,"app");
server.createWebInf();
server.copyEndpoint(ConfiguredEchoSocket.class);

View File

@ -0,0 +1,100 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidCloseIntSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorErrorSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorExceptionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorIntSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidOpenCloseReasonSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidOpenIntSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidOpenSessionIntSocket;
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Deploy various {@link ServerEndpoint} annotated classes with invalid signatures,
* check for {@link DeploymentException}
*/
@RunWith(Parameterized.class)
public class DeploymentExceptionTest
{
@Parameters(name = "{0}")
public static Collection<Class<?>[]> data()
{
List<Class<?>[]> data = new ArrayList<>();
data.add(new Class<?>[]{InvalidCloseIntSocket.class});
data.add(new Class<?>[]{InvalidErrorErrorSocket.class});
data.add(new Class<?>[]{InvalidErrorExceptionSocket.class});
data.add(new Class<?>[]{InvalidErrorIntSocket.class});
data.add(new Class<?>[]{InvalidOpenCloseReasonSocket.class});
data.add(new Class<?>[]{InvalidOpenIntSocket.class});
data.add(new Class<?>[]{InvalidOpenSessionIntSocket.class});
// TODO: invalid return types
// TODO: static methods
// TODO: private or protected methods
// TODO: abstract methods
return data;
}
@Rule
public ExpectedException expectedException = ExpectedException.none();
/** The pojo to test */
@Parameterized.Parameter(0)
public Class<?> pojo;
@Test
public void testDeploy_InvalidSignature() throws Exception
{
MappedWebSocketCreator creator = new DummyCreator();
WebSocketServerFactory serverFactory = new WebSocketServerFactory();
Executor executor = new QueuedThreadPool();
ServerContainer container = new ServerContainer(creator, serverFactory, executor);
try
{
container.start();
expectedException.expect(DeploymentException.class);
container.addEndpoint(pojo);
}
finally
{
container.stop();
}
}
}

View File

@ -95,6 +95,16 @@ public class EchoClientSocket extends TrackingSocket
{
return eventCountLatch.await(timeout,unit);
}
public void sendText(String msg) throws IOException, EncodeException
{
remote.sendText(msg);
}
public void sendBinary(ByteBuffer msg) throws IOException, EncodeException
{
remote.sendBinary(msg);
}
public void sendObject(Object obj) throws IOException, EncodeException
{

View File

@ -18,10 +18,12 @@
package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.contains;
import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -36,6 +38,21 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.jsr356.server.EchoCase.PartialBinary;
import org.eclipse.jetty.websocket.jsr356.server.EchoCase.PartialText;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseReasonSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseReasonSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseSessionReasonSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorSessionThrowableSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorThrowableSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorThrowableSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicOpenSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicOpenSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicPongMessageSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicTextMessageStringSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.StatelessTextMessageStringSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.DateTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.binary.ByteBufferSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.partial.PartialTextSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.partial.PartialTextSocket;
@ -61,7 +78,6 @@ import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.ReaderParamSo
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.ReaderSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.StringReturnReaderParamSocket;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -182,6 +198,10 @@ public class EchoTest
// PathParam based
EchoCase.add(TESTCASES,IntParamTextSocket.class).requestPath("/echo/primitives/integer/params/5678").addMessage(1234).expect("1234|5678");
// Text based
EchoCase.add(TESTCASES,BasicTextMessageStringSocket.class).addMessage("Hello").expect("Hello");
EchoCase.add(TESTCASES,StatelessTextMessageStringSocket.class).addMessage("Hello").expect("Hello");
// ByteBuffer based
EchoCase.add(TESTCASES,ByteBufferSocket.class).addMessage(BufferUtil.toBuffer("Hello World")).expect("Hello World");
@ -201,12 +221,35 @@ public class EchoTest
EchoCase.add(TESTCASES,PartialTextSessionSocket.class)
.addSplitMessage("Built"," for"," the"," future")
.expect("('Built',false)(' for',false)(' the',false)(' future',true)");
// Beans
EchoCase.add(TESTCASES, DateTextSocket.class).addMessage("Ooops").expect("");
// Pong
EchoCase.add(TESTCASES, BasicPongMessageSocket.class).addMessage("send-ping").expect("Pong[]");
// Open Events
EchoCase.add(TESTCASES, BasicOpenSocket.class).expect("Open[]");
EchoCase.add(TESTCASES, BasicOpenSessionSocket.class).expect("Open[Session]");
// Close Events
EchoCase.add(TESTCASES, BasicCloseSocket.class).expect("Close[]");
EchoCase.add(TESTCASES, BasicCloseReasonSocket.class).expect("Close[Reason]");
EchoCase.add(TESTCASES, BasicCloseReasonSessionSocket.class).expect("Close[Reason,Session]");
EchoCase.add(TESTCASES, BasicCloseSessionReasonSocket.class).expect("Close[Session,Reason]");
// Error Events
EchoCase.add(TESTCASES, BasicErrorSocket.class).expect("Error[]");
EchoCase.add(TESTCASES, BasicErrorSessionSocket.class).expect("Error[Session]");
EchoCase.add(TESTCASES, BasicErrorSessionThrowableSocket.class).expect("Error[Session,Throwable]");
EchoCase.add(TESTCASES, BasicErrorThrowableSocket.class).expect("Error[Throwable]");
EchoCase.add(TESTCASES, BasicErrorThrowableSessionSocket.class).expect("Error[Throwable,Session]");
}
@BeforeClass
public static void startServer() throws Exception
{
File testdir = MavenTestingUtils.getTargetTestingDir(EchoTest.class.getName());
Path testdir = MavenTestingUtils.getTargetTestingPath(EchoTest.class.getName());
server = new WSServer(testdir,"app");
server.copyWebInf("empty-web.xml");
@ -277,6 +320,14 @@ public class EchoTest
PartialBinary pb = (PartialBinary)msg;
socket.sendPartialBinary(pb.part,pb.fin);
}
else if (msg instanceof ByteBuffer)
{
socket.sendBinary((ByteBuffer) msg);
}
else if (msg instanceof String)
{
socket.sendText((String) msg);
}
else
{
socket.sendObject(msg);
@ -286,12 +337,9 @@ public class EchoTest
// Collect Responses
socket.awaitAllEvents(1,TimeUnit.SECONDS);
EventQueue<String> received = socket.eventQueue;
// Validate Responses
for (String expected : testcase.expectedStrings)
{
Assert.assertThat("Received Echo Responses",received,contains(expected));
}
assertThat("Received Events", received, ordered(testcase.expectedStrings));
}
finally
{

View File

@ -31,11 +31,10 @@ import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.IdleTimeoutContextListener;
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutEndpoint;
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutSocket;
@ -52,7 +51,7 @@ public class IdleTimeoutTest
@BeforeClass
public static void setupServer() throws Exception
{
server = new WSServer(MavenTestingUtils.getTargetTestingDir(IdleTimeoutTest.class.getName()),"app");
server = new WSServer(MavenTestingUtils.getTargetTestingPath(IdleTimeoutTest.class.getName()),"app");
server.copyWebInf("idle-timeout-config-web.xml");
// the endpoint (extends javax.websocket.Endpoint)
server.copyClass(OnOpenIdleTimeoutEndpoint.class);
@ -118,11 +117,8 @@ public class IdleTimeoutTest
@Test
public void testAnnotated() throws Exception
{
try(StacklessLogging ignored = new StacklessLogging(JsrEvents.class))
{
URI uri = server.getServerBaseURI();
assertConnectionTimeout(uri.resolve("idle-onopen-socket"));
}
URI uri = server.getServerBaseURI();
assertConnectionTimeout(uri.resolve("idle-onopen-socket"));
}
@Test

View File

@ -1,120 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.common.test.DummyConnection;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.server.samples.partial.PartialTrackingSocket;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
public class OnPartialTest
{
@Rule
public TestName testname = new TestName();
public EventDriver toEventDriver(Object websocket) throws Throwable
{
WebSocketPolicy policy = WebSocketPolicy.newServerPolicy();
policy.setInputBufferSize(1024);
policy.setMaxBinaryMessageBufferSize(1024);
policy.setMaxTextMessageBufferSize(1024);
// Create EventDriver
EventDriverImpl driverImpl = new JsrServerEndpointImpl();
Class<?> endpoint = websocket.getClass();
ServerEndpoint anno = endpoint.getAnnotation(ServerEndpoint.class);
Assert.assertThat("Endpoint: " + endpoint + " should be annotated with @ServerEndpoint",anno,notNullValue());
WebSocketContainerScope containerScope = new SimpleContainerScope(policy);
// Event Driver Factory
EventDriverFactory factory = new EventDriverFactory(containerScope);
factory.addImplementation(new JsrServerEndpointImpl());
ServerEndpointConfig config = new BasicServerEndpointConfig(containerScope,endpoint,"/");
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(containerScope,endpoint,config);
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
scanner.scan();
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket,config,metadata);
EventDriver driver = driverImpl.create(ei,policy);
Assert.assertThat("EventDriver",driver,notNullValue());
// Create Local JsrSession
String id = testname.getMethodName();
URI requestURI = URI.create("ws://localhost/" + id);
DummyConnection connection = new DummyConnection();
ClientContainer container = new ClientContainer();
container.start();
@SuppressWarnings("resource")
JsrSession session = new JsrSession(container,id,requestURI,driver,connection);
session.start();
session.open();
return driver;
}
@Test
public void testOnTextPartial() throws Throwable
{
List<WebSocketFrame> frames = new ArrayList<>();
frames.add(new TextFrame().setPayload("Saved").setFin(false));
frames.add(new ContinuationFrame().setPayload(" by ").setFin(false));
frames.add(new ContinuationFrame().setPayload("zero").setFin(true));
PartialTrackingSocket socket = new PartialTrackingSocket();
EventDriver driver = toEventDriver(socket);
driver.onConnect();
for (WebSocketFrame frame : frames)
{
driver.incomingFrame(frame);
}
Assert.assertThat("Captured Event Queue size",socket.eventQueue.size(),is(3));
Assert.assertThat("Event[0]",socket.eventQueue.poll(),is("onPartial(\"Saved\",false)"));
Assert.assertThat("Event[1]",socket.eventQueue.poll(),is("onPartial(\" by \",false)"));
Assert.assertThat("Event[2]",socket.eventQueue.poll(),is("onPartial(\"zero\",true)"));
}
}

View File

@ -21,8 +21,8 @@ package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import javax.websocket.ContainerProvider;
@ -48,7 +48,7 @@ public class PingPongTest
@BeforeClass
public static void startServer() throws Exception
{
File testdir = MavenTestingUtils.getTargetTestingDir(PingPongTest.class.getName());
Path testdir = MavenTestingUtils.getTargetTestingPath(PingPongTest.class.getName());
server = new WSServer(testdir,"app");
server.copyWebInf("pong-config-web.xml");

View File

@ -1,206 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.notNullValue;
import java.io.Reader;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.websocket.CloseReason;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicBinaryMessageByteBufferSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseReasonSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseReasonSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseSessionReasonSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicCloseSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorSessionThrowableSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorThrowableSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicErrorThrowableSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicOpenSessionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicOpenSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicPongMessageSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicTextMessageStringSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.StatelessTextMessageStringSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.DateTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.BooleanObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.BooleanTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.ByteObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.ByteTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.CharTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.CharacterObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.DoubleObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.DoubleTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.FloatObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.FloatTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.IntTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.IntegerObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.ShortObjectTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.primitives.ShortTextSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.ReaderParamSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.StringReturnReaderParamSocket;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Test {@link AnnotatedEndpointScanner} against various simple, 1 method {@link ServerEndpoint} annotated classes with valid signatures.
*/
@RunWith(Parameterized.class)
public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
{
public static class Case
{
public static void add(List<Case[]> data, Class<?> pojo, Field metadataField, Class<?>... expectedParams)
{
data.add(new Case[]
{ new Case(pojo,metadataField,expectedParams) });
}
// The websocket pojo to test against
Class<?> pojo;
// The JsrAnnotatedMetadata field that should be populated
Field metadataField;
// The expected parameters for the Callable found by the scanner
Class<?> expectedParameters[];
public Case(Class<?> pojo, Field metadataField, Class<?>... expectedParams)
{
this.pojo = pojo;
this.metadataField = metadataField;
this.expectedParameters = expectedParams;
}
}
@Parameters
public static Collection<Case[]> data() throws Exception
{
List<Case[]> data = new ArrayList<>();
Field fOpen = findFieldRef(AnnotatedServerEndpointMetadata.class,"onOpen");
Field fClose = findFieldRef(AnnotatedServerEndpointMetadata.class,"onClose");
Field fError = findFieldRef(AnnotatedServerEndpointMetadata.class,"onError");
Field fText = findFieldRef(AnnotatedServerEndpointMetadata.class,"onText");
Field fTextStream = findFieldRef(AnnotatedServerEndpointMetadata.class,"onTextStream");
Field fBinary = findFieldRef(AnnotatedServerEndpointMetadata.class,"onBinary");
@SuppressWarnings("unused")
Field fBinaryStream = findFieldRef(AnnotatedServerEndpointMetadata.class,"onBinaryStream");
Field fPong = findFieldRef(AnnotatedServerEndpointMetadata.class,"onPong");
// @formatter:off
// -- Open Events
Case.add(data, BasicOpenSocket.class, fOpen);
Case.add(data, BasicOpenSessionSocket.class, fOpen, Session.class);
// -- Close Events
Case.add(data, BasicCloseSocket.class, fClose);
Case.add(data, BasicCloseReasonSocket.class, fClose, CloseReason.class);
Case.add(data, BasicCloseReasonSessionSocket.class, fClose, CloseReason.class, Session.class);
Case.add(data, BasicCloseSessionReasonSocket.class, fClose, Session.class, CloseReason.class);
// -- Error Events
Case.add(data, BasicErrorSocket.class, fError);
Case.add(data, BasicErrorSessionSocket.class, fError, Session.class);
Case.add(data, BasicErrorSessionThrowableSocket.class, fError, Session.class, Throwable.class);
Case.add(data, BasicErrorThrowableSocket.class, fError, Throwable.class);
Case.add(data, BasicErrorThrowableSessionSocket.class, fError, Throwable.class, Session.class);
// -- Text Events
Case.add(data, BasicTextMessageStringSocket.class, fText, String.class);
Case.add(data, StatelessTextMessageStringSocket.class, fText, Session.class, String.class);
// -- Primitives
Case.add(data, BooleanTextSocket.class, fText, Boolean.TYPE);
Case.add(data, BooleanObjectTextSocket.class, fText, Boolean.class);
Case.add(data, ByteTextSocket.class, fText, Byte.TYPE);
Case.add(data, ByteObjectTextSocket.class, fText, Byte.class);
Case.add(data, CharTextSocket.class, fText, Character.TYPE);
Case.add(data, CharacterObjectTextSocket.class, fText, Character.class);
Case.add(data, DoubleTextSocket.class, fText, Double.TYPE);
Case.add(data, DoubleObjectTextSocket.class, fText, Double.class);
Case.add(data, FloatTextSocket.class, fText, Float.TYPE);
Case.add(data, FloatObjectTextSocket.class, fText, Float.class);
Case.add(data, IntTextSocket.class, fText, Integer.TYPE);
Case.add(data, IntegerObjectTextSocket.class, fText, Integer.class);
Case.add(data, ShortTextSocket.class, fText, Short.TYPE);
Case.add(data, ShortObjectTextSocket.class, fText, Short.class);
// -- Beans
Case.add(data, DateTextSocket.class, fText, Date.class);
// -- Reader Events
Case.add(data, ReaderParamSocket.class, fTextStream, Reader.class, String.class);
Case.add(data, StringReturnReaderParamSocket.class, fTextStream, Reader.class, String.class);
// -- Binary Events
Case.add(data, BasicBinaryMessageByteBufferSocket.class, fBinary, ByteBuffer.class);
// -- Pong Events
Case.add(data, BasicPongMessageSocket.class, fPong, PongMessage.class);
// @formatter:on
// TODO: validate return types
return data;
}
private static Field findFieldRef(Class<?> clazz, String fldName) throws Exception
{
return clazz.getField(fldName);
}
private Case testcase;
public ServerAnnotatedEndpointScanner_GoodSignaturesTest(Case testcase)
{
this.testcase = testcase;
System.err.printf("Testing signature of %s%n",testcase.pojo.getName());
}
@Test
public void testScan_Basic() throws Exception
{
WebSocketContainerScope container = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(container,testcase.pojo,null);
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
scanner.scan();
Assert.assertThat("Metadata",metadata,notNullValue());
JsrCallable method = (JsrCallable)testcase.metadataField.get(metadata);
Assert.assertThat(testcase.metadataField.toString(),method,notNullValue());
int len = testcase.expectedParameters.length;
for (int i = 0; i < len; i++)
{
Class<?> expectedParam = testcase.expectedParameters[i];
Class<?> actualParam = method.getParamTypes()[i];
Assert.assertTrue("Parameter[" + i + "] - expected:[" + expectedParam + "], actual:[" + actualParam + "]",actualParam.equals(expectedParam));
}
}
}

View File

@ -1,116 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.containsString;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidCloseIntSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorErrorSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorExceptionSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorIntSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidOpenCloseReasonSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidOpenIntSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidOpenSessionIntSocket;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Test {@link AnnotatedEndpointScanner} against various simple, 1 method {@link ServerEndpoint} annotated classes with invalid signatures.
*/
@RunWith(Parameterized.class)
public class ServerAnnotatedEndpointScanner_InvalidSignaturesTest
{
private static final Logger LOG = Log.getLogger(ServerAnnotatedEndpointScanner_InvalidSignaturesTest.class);
@Parameters
public static Collection<Class<?>[]> data()
{
List<Class<?>[]> data = new ArrayList<>();
// @formatter:off
data.add(new Class<?>[]{ InvalidCloseIntSocket.class, OnClose.class });
data.add(new Class<?>[]{ InvalidErrorErrorSocket.class, OnError.class });
data.add(new Class<?>[]{ InvalidErrorExceptionSocket.class, OnError.class });
data.add(new Class<?>[]{ InvalidErrorIntSocket.class, OnError.class });
data.add(new Class<?>[]{ InvalidOpenCloseReasonSocket.class, OnOpen.class });
data.add(new Class<?>[]{ InvalidOpenIntSocket.class, OnOpen.class });
data.add(new Class<?>[]{ InvalidOpenSessionIntSocket.class, OnOpen.class });
// @formatter:on
// TODO: invalid return types
// TODO: static methods
// TODO: private or protected methods
// TODO: abstract methods
return data;
}
// The pojo to test
private Class<?> pojo;
// The annotation class expected to be mentioned in the error message
private Class<? extends Annotation> expectedAnnoClass;
public ServerAnnotatedEndpointScanner_InvalidSignaturesTest(Class<?> pojo, Class<? extends Annotation> expectedAnnotation)
{
this.pojo = pojo;
this.expectedAnnoClass = expectedAnnotation;
}
@Test
public void testScan_InvalidSignature() throws DeploymentException
{
WebSocketContainerScope container = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(container,pojo,null);
AnnotatedEndpointScanner<ServerEndpoint,ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
try
{
scanner.scan();
Assert.fail("Expected " + InvalidSignatureException.class + " with message that references " + expectedAnnoClass + " annotation");
}
catch (InvalidSignatureException e)
{
if (LOG.isDebugEnabled())
LOG.debug("{}:{}",e.getClass(),e.getMessage());
Assert.assertThat("Message",e.getMessage(),containsString(expectedAnnoClass.getSimpleName()));
}
}
}

View File

@ -114,7 +114,7 @@ public class SessionTest
@Before
public void startServer() throws Exception
{
server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName() + "-" + ID.incrementAndGet()),"app");
server = new WSServer(MavenTestingUtils.getTargetTestingPath(SessionTest.class.getSimpleName() + "-" + ID.incrementAndGet()),"app");
server.copyWebInf("empty-web.xml");
server.copyClass(SessionInfoSocket.class);
server.copyClass(SessionAltConfig.class);

View File

@ -25,6 +25,7 @@ import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
@ -36,11 +37,10 @@ import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
@ -57,22 +57,22 @@ import org.junit.Assert;
public class WSServer
{
private static final Logger LOG = Log.getLogger(WSServer.class);
private final File contextDir;
private final Path contextDir;
private final String contextPath;
private Server server;
private URI serverUri;
private ContextHandlerCollection contexts;
private File webinf;
private File classesDir;
private Path webinf;
private Path classesDir;
public WSServer(TestingDir testdir, String contextName)
{
this(testdir.getPath().toFile(),contextName);
this(testdir.getPath(),contextName);
}
public WSServer(File testdir, String contextName)
public WSServer(Path testdir, String contextName)
{
this.contextDir = new File(testdir,contextName);
this.contextDir = testdir.resolve(contextName);
this.contextPath = "/" + contextName;
FS.ensureEmpty(contextDir);
}
@ -83,10 +83,10 @@ public class WSServer
String endpointPath = clazz.getName().replace('.','/') + ".class";
URL classUrl = cl.getResource(endpointPath);
Assert.assertThat("Class URL for: " + clazz,classUrl,notNullValue());
File destFile = new File(classesDir,OS.separators(endpointPath));
FS.ensureDirExists(destFile.getParentFile());
Path destFile = classesDir.resolve(endpointPath);
FS.ensureDirExists(destFile.getParent());
File srcFile = new File(classUrl.toURI());
IO.copy(srcFile,destFile);
IO.copy(srcFile,destFile.toFile());
}
public void copyEndpoint(Class<?> endpointClass) throws Exception
@ -96,20 +96,20 @@ public class WSServer
public void copyWebInf(String testResourceName) throws IOException
{
webinf = new File(contextDir,"WEB-INF");
webinf = contextDir.resolve("WEB-INF");
FS.ensureDirExists(webinf);
classesDir = new File(webinf,"classes");
classesDir = webinf.resolve("classes");
FS.ensureDirExists(classesDir);
File webxml = new File(webinf,"web.xml");
Path webxml = webinf.resolve("web.xml");
File testWebXml = MavenTestingUtils.getTestResourceFile(testResourceName);
IO.copy(testWebXml,webxml);
IO.copy(testWebXml,webxml.toFile());
}
public WebAppContext createWebAppContext() throws MalformedURLException, IOException
{
WebAppContext context = new WebAppContext();
context.setContextPath(this.contextPath);
context.setBaseResource(Resource.newResource(this.contextDir));
context.setBaseResource(new PathResource(this.contextDir));
context.setAttribute("org.eclipse.jetty.websocket.jsr356",Boolean.TRUE);
// @formatter:off
@ -157,7 +157,7 @@ public class WSServer
return server;
}
public File getWebAppDir()
public Path getWebAppDir()
{
return this.contextDir;
}