453834 - CDI Support for WebSocket

+ Attempting to get new socket @Inject working
This commit is contained in:
Joakim Erdfelt 2014-12-03 14:24:08 -07:00
parent 5c3e30d136
commit d6911b431f
17 changed files with 232 additions and 31 deletions

View File

@ -23,6 +23,9 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* An instantiator enhanced by {@link Decorator} instances.
* <p>
@ -33,7 +36,42 @@ import java.util.List;
*/
public class EnhancedInstantiator implements Iterable<Decorator>
{
private static final ThreadLocal<EnhancedInstantiator> CURRENT_INSTANTIATOR = new ThreadLocal<>();
/**
* Get the current EnhancedInstantiator that this thread is dispatched to.
* <p>
* This exists because of various {@link java.util.ServiceLoader} use that makes passing in an EnhancedInstantiator
* difficult.
*
* @return the current EnhancedInstantiator or null
*/
public static EnhancedInstantiator getCurrentInstantiator()
{
return CURRENT_INSTANTIATOR.get();
}
public static EnhancedInstantiator setCurrentInstantiator(EnhancedInstantiator instantiator)
{
EnhancedInstantiator last = CURRENT_INSTANTIATOR.get();
if (instantiator == null)
{
CURRENT_INSTANTIATOR.remove();
}
else
{
CURRENT_INSTANTIATOR.set(instantiator);
}
return last;
}
private static final Logger LOG = Log.getLogger(EnhancedInstantiator.class);
/**
* ServletContext attribute for the active EnhancedInstantiator
*/
public static final String ATTR = EnhancedInstantiator.class.getName();
private List<Decorator> decorators = new ArrayList<>();
public void addDecorator(Decorator decorator)
@ -48,6 +86,10 @@ public class EnhancedInstantiator implements Iterable<Decorator>
public <T> T createInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
{
if (LOG.isDebugEnabled())
{
LOG.debug("Creating Instance: " + clazz,new Throwable("Creation Stack"));
}
T o = clazz.newInstance();
return decorate(o);
}

View File

@ -85,6 +85,8 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
/** The jetty websocket client in use for this container */
private WebSocketClient client;
protected EnhancedInstantiator enhancedInstantiator;
public ClientContainer()
{
// This constructor is used with Standalone JSR Client usage.
@ -94,13 +96,14 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
public ClientContainer(Executor executor, EnhancedInstantiator enhancedInstantiator)
{
endpointClientMetadataCache = new ConcurrentHashMap<>();
decoderFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE,null,enhancedInstantiator);
encoderFactory = new EncoderFactory(PrimitiveEncoderMetadataSet.INSTANCE,null,enhancedInstantiator);
this.enhancedInstantiator = enhancedInstantiator;
this.endpointClientMetadataCache = new ConcurrentHashMap<>();
this.decoderFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE,null,enhancedInstantiator);
this.encoderFactory = new EncoderFactory(PrimitiveEncoderMetadataSet.INSTANCE,null,enhancedInstantiator);
EmptyClientEndpointConfig empty = new EmptyClientEndpointConfig();
decoderFactory.init(empty);
encoderFactory.init(empty);
this.decoderFactory.init(empty);
this.encoderFactory.init(empty);
boolean trustAll = Boolean.getBoolean("org.eclipse.jetty.websocket.jsr356.ssl-trust-all");

View File

@ -32,6 +32,8 @@ import javax.websocket.Extension;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
{
private final Class<?> endpointClass;
@ -44,12 +46,12 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
private Map<String, Object> userProperties;
private List<Extension> extensions;
public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno, EnhancedInstantiator enhancedInstantiator) throws DeploymentException
{
this(endpointClass,anno,null);
this(endpointClass,anno,enhancedInstantiator,null);
}
public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno, ServerEndpointConfig baseConfig) throws DeploymentException
public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno, EnhancedInstantiator enhancedInstantiator, ServerEndpointConfig baseConfig) throws DeploymentException
{
ServerEndpointConfig.Configurator configr = null;
@ -118,7 +120,7 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
}
else
{
this.configurator = BasicServerEndpointConfigurator.INSTANCE;
this.configurator = new BasicServerEndpointConfigurator(enhancedInstantiator);
}
}
else

View File

@ -24,6 +24,7 @@ import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
@ -33,7 +34,7 @@ public class AnnotatedServerEndpointMetadata extends AnnotatedEndpointMetadata<S
private final ServerEndpoint endpoint;
private final AnnotatedServerEndpointConfig config;
protected AnnotatedServerEndpointMetadata(Class<?> websocket, ServerEndpointConfig baseConfig) throws DeploymentException
protected AnnotatedServerEndpointMetadata(Class<?> websocket, ServerEndpointConfig baseConfig, EnhancedInstantiator enhancedInstantiator) throws DeploymentException
{
super(websocket);
@ -44,7 +45,7 @@ public class AnnotatedServerEndpointMetadata extends AnnotatedEndpointMetadata<S
}
this.endpoint = anno;
this.config = new AnnotatedServerEndpointConfig(websocket,anno,baseConfig);
this.config = new AnnotatedServerEndpointConfig(websocket,anno,enhancedInstantiator,baseConfig);
getDecoders().addAll(anno.decoders());
getEncoders().addAll(anno.encoders());

View File

@ -28,6 +28,8 @@ import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
public class BasicServerEndpointConfig implements ServerEndpointConfig
{
private final List<Class<? extends Decoder>> decoders;
@ -39,7 +41,7 @@ public class BasicServerEndpointConfig implements ServerEndpointConfig
private final String path;
private Map<String, Object> userProperties;
public BasicServerEndpointConfig(Class<?> endpointClass, String path)
public BasicServerEndpointConfig(Class<?> endpointClass, String path, EnhancedInstantiator enhancedInstantiator)
{
this.endpointClass = endpointClass;
this.path = path;
@ -49,10 +51,10 @@ public class BasicServerEndpointConfig implements ServerEndpointConfig
this.subprotocols = new ArrayList<>();
this.extensions = new ArrayList<>();
this.userProperties = new HashMap<>();
this.configurator = BasicServerEndpointConfigurator.INSTANCE;
this.configurator = new BasicServerEndpointConfigurator(enhancedInstantiator);
}
public BasicServerEndpointConfig(ServerEndpointConfig copy)
public BasicServerEndpointConfig(ServerEndpointConfig copy, EnhancedInstantiator enhancedInstantiator)
{
// immutable concepts
this.endpointClass = copy.getEndpointClass();
@ -68,7 +70,7 @@ public class BasicServerEndpointConfig implements ServerEndpointConfig
}
else
{
this.configurator = BasicServerEndpointConfigurator.INSTANCE;
this.configurator = new BasicServerEndpointConfigurator(enhancedInstantiator);
}
// mutable concepts

View File

@ -19,11 +19,14 @@
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.List;
import java.util.Objects;
import javax.websocket.Extension;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.util.QuoteUtil;
@ -32,7 +35,23 @@ public class BasicServerEndpointConfigurator extends ServerEndpointConfig.Config
{
private static final Logger LOG = Log.getLogger(BasicServerEndpointConfigurator.class);
private static final String NO_SUBPROTOCOL = "";
public static final ServerEndpointConfig.Configurator INSTANCE = new BasicServerEndpointConfigurator();
private final EnhancedInstantiator enhancedInstantiator;
/**
* Default Constructor required, as
* javax.websocket.server.ServerEndpointConfig$Configurator.fetchContainerDefaultConfigurator()
* will be the one that instantiates this class in most cases.
*/
public BasicServerEndpointConfigurator()
{
this(EnhancedInstantiator.getCurrentInstantiator());
}
public BasicServerEndpointConfigurator(EnhancedInstantiator enhancedInstantiator)
{
Objects.requireNonNull(enhancedInstantiator,"EnhancedInstantiator cannot be null");
this.enhancedInstantiator = enhancedInstantiator;
}
@Override
public boolean checkOrigin(String originHeaderValue)
@ -47,9 +66,10 @@ public class BasicServerEndpointConfigurator extends ServerEndpointConfig.Config
{
LOG.debug(".getEndpointInstance({})",endpointClass);
}
try
{
return endpointClass.newInstance();
return enhancedInstantiator.createInstance(endpointClass);
}
catch (IllegalAccessException e)
{
@ -105,4 +125,4 @@ public class BasicServerEndpointConfigurator extends ServerEndpointConfig.Config
{
/* do nothing */
}
}
}

View File

@ -26,6 +26,7 @@ import javax.websocket.Extension;
import javax.websocket.Extension.Parameter;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -46,11 +47,13 @@ public class JsrCreator implements WebSocketCreator
private static final Logger LOG = Log.getLogger(JsrCreator.class);
private final ServerEndpointMetadata metadata;
private final ExtensionFactory extensionFactory;
private final EnhancedInstantiator enhancedInstantiator;
public JsrCreator(ServerEndpointMetadata metadata, ExtensionFactory extensionFactory)
public JsrCreator(ServerEndpointMetadata metadata, ExtensionFactory extensionFactory, EnhancedInstantiator enhancedInstantiator)
{
this.metadata = metadata;
this.extensionFactory = extensionFactory;
this.enhancedInstantiator = enhancedInstantiator;
}
@Override
@ -64,7 +67,7 @@ public class JsrCreator implements WebSocketCreator
// Establish a copy of the config, so that the UserProperties are unique
// per upgrade request.
config = new BasicServerEndpointConfig(config);
config = new BasicServerEndpointConfig(config, enhancedInstantiator);
// Bug 444617 - Expose localAddress and remoteAddress for jsr modify handshake to use
// This is being implemented as an optional set of userProperties so that
@ -142,7 +145,7 @@ public class JsrCreator implements WebSocketCreator
WebSocketPathSpec wspathSpec = (WebSocketPathSpec)pathSpec;
String requestPath = req.getRequestPath();
// Wrap the config with the path spec information
config = new PathParamServerEndpointConfig(config,wspathSpec,requestPath);
config = new PathParamServerEndpointConfig(config,enhancedInstantiator,wspathSpec,requestPath);
}
return new EndpointInstance(endpoint,config,metadata);
}

View File

@ -23,6 +23,7 @@ import java.util.Map;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
/**
@ -32,9 +33,9 @@ public class PathParamServerEndpointConfig extends BasicServerEndpointConfig imp
{
private final Map<String, String> pathParamMap;
public PathParamServerEndpointConfig(ServerEndpointConfig config, WebSocketPathSpec pathSpec, String requestPath)
public PathParamServerEndpointConfig(ServerEndpointConfig config, EnhancedInstantiator enhancedInstantiator, WebSocketPathSpec pathSpec, String requestPath)
{
super(config);
super(config, enhancedInstantiator);
Map<String, String> pathMap = pathSpec.getPathParams(requestPath);
pathParamMap = new HashMap<String, String>();

View File

@ -67,7 +67,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
}
else
{
cec = new BasicServerEndpointConfig(endpoint.getClass(),path);
cec = new BasicServerEndpointConfig(endpoint.getClass(),path,enhancedInstantiator);
}
}
return new EndpointInstance(endpoint,cec,metadata);
@ -82,7 +82,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
public void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
{
JsrCreator creator = new JsrCreator(metadata,webSocketServerFactory.getExtensionFactory());
JsrCreator creator = new JsrCreator(metadata,webSocketServerFactory.getExtensionFactory(),enhancedInstantiator);
mappedCreator.addMapping(new WebSocketPathSpec(metadata.getPath()),creator);
}
@ -105,7 +105,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
if (anno != null)
{
// Annotated takes precedence here
AnnotatedServerEndpointMetadata ametadata = new AnnotatedServerEndpointMetadata(endpoint,config);
AnnotatedServerEndpointMetadata ametadata = new AnnotatedServerEndpointMetadata(endpoint,config,enhancedInstantiator);
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(ametadata);
metadata = ametadata;
scanner.scan();

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.server.deploy;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@ -32,6 +33,7 @@ import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.EnhancedInstantiator;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -165,6 +167,17 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
// Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
// Establish the EnhancedInstantiator thread local
// for various ServiceLoader initiated components to use.
EnhancedInstantiator instantiator = (EnhancedInstantiator)context.getAttribute(EnhancedInstantiator.ATTR);
if (instantiator == null)
{
LOG.info("Using WebSocket local EnhancedInstantiator - none found in ServletContext");
instantiator = new EnhancedInstantiator();
}
EnhancedInstantiator.setCurrentInstantiator(instantiator);
if (LOG.isDebugEnabled())
{

View File

@ -67,8 +67,11 @@ public class OnPartialTest
Class<?> endpoint = websocket.getClass();
ServerEndpoint anno = endpoint.getAnnotation(ServerEndpoint.class);
Assert.assertThat("Endpoint: " + endpoint + " should be annotated with @ServerEndpoint",anno,notNullValue());
ServerEndpointConfig config = new BasicServerEndpointConfig(endpoint,"/");
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(endpoint,config);
EnhancedInstantiator instantiator = new EnhancedInstantiator();
ServerEndpointConfig config = new BasicServerEndpointConfig(endpoint,"/",instantiator);
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(endpoint,config,instantiator);
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
scanner.scan();
EndpointInstance ei = new EndpointInstance(websocket,config,metadata);

View File

@ -34,6 +34,7 @@ import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
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;
@ -182,7 +183,7 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
@Test
public void testScan_Basic() throws Exception
{
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(testcase.pojo,null);
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(testcase.pojo,null,new EnhancedInstantiator());
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
scanner.scan();

View File

@ -22,6 +22,7 @@ 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;
@ -29,6 +30,7 @@ import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.util.EnhancedInstantiator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
@ -93,7 +95,7 @@ public class ServerAnnotatedEndpointScanner_InvalidSignaturesTest
@Test
public void testScan_InvalidSignature() throws DeploymentException
{
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(pojo,null);
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(pojo,null,new EnhancedInstantiator());
AnnotatedEndpointScanner<ServerEndpoint,ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
try

View File

@ -164,6 +164,12 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
try
{
Thread.currentThread().setContextClassLoader(contextClassloader);
// Establish the EnhancedInstantiator thread local
// for various ServiceLoader initiated components to use.
EnhancedInstantiator.setCurrentInstantiator(getEnhancedInstantiator(request));
// Create Servlet Specific Upgrade Request/Response objects
ServletUpgradeRequest sockreq = new ServletUpgradeRequest(request);
ServletUpgradeResponse sockresp = new ServletUpgradeResponse(response);
@ -335,6 +341,45 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
return enhancedInstantiator;
}
public EnhancedInstantiator getEnhancedInstantiator(HttpServletRequest request)
{
if (enhancedInstantiator != null)
{
return enhancedInstantiator;
}
if (request == null)
{
LOG.debug("Using default EnhancedInstantiator (HttpServletRequest is null)");
return new EnhancedInstantiator();
}
return getEnhancedInstantiator(request.getServletContext());
}
public EnhancedInstantiator getEnhancedInstantiator(ServletContext context)
{
if (enhancedInstantiator != null)
{
return enhancedInstantiator;
}
if (context == null)
{
LOG.debug("Using default EnhancedInstantiator (ServletContext is null)");
return new EnhancedInstantiator();
}
enhancedInstantiator = (EnhancedInstantiator)context.getAttribute(EnhancedInstantiator.ATTR);
if (enhancedInstantiator == null)
{
LOG.debug("Using default EnhancedInstantiator (ServletContext attribute is null)");
enhancedInstantiator = new EnhancedInstantiator();
}
return enhancedInstantiator;
}
public EventDriverFactory getEventDriverFactory()
{
return eventDriverFactory;

View File

@ -12,7 +12,7 @@ cd "$JETTY_BASE"
"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
--approve-all-licenses \
--add-to-start=deploy,http,annotations,cdi,logging
--add-to-start=deploy,http,annotations,websocket,cdi,logging
"$JAVA_HOME/bin/java" -jar "$JETTY_HOME/start.jar" \
--version

View File

@ -38,6 +38,11 @@
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.tests.ws;
import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/sessioninfo")
public class SessionInfoSocket
{
@Inject
private HttpSession httpSession;
@OnMessage
public void onMessage(Session session, String message)
{
try
{
switch (message)
{
case "info":
session.getBasicRemote().sendText("HttpSession = " + httpSession);
break;
case "close":
session.close();
break;
default:
session.getBasicRemote().sendText(message);
break;
}
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}