JSR-356: adding WebSocketConfiguration and reworking ServerContainer init

This commit is contained in:
Joakim Erdfelt 2013-04-24 14:33:46 -07:00
parent cab4826c08
commit c91c3f2f60
29 changed files with 474 additions and 260 deletions

View File

@ -70,11 +70,14 @@ public class Decoders
Objects.requireNonNull(metadataFactory,"DecoderMetadataFactory cannot be null"); Objects.requireNonNull(metadataFactory,"DecoderMetadataFactory cannot be null");
this.metadataFactory = metadataFactory; this.metadataFactory = metadataFactory;
if (config != null)
{
for (Class<? extends Decoder> decoder : config.getDecoders()) for (Class<? extends Decoder> decoder : config.getDecoders())
{ {
addAllMetadata(decoder); addAllMetadata(decoder);
} }
} }
}
public void add(DecoderWrapper wrapper) throws IllegalStateException public void add(DecoderWrapper wrapper) throws IllegalStateException
{ {
@ -153,6 +156,14 @@ public class Decoders
throw new InvalidSignatureException("Unable to find appropriate Decoder for type: " + type); throw new InvalidSignatureException("Unable to find appropriate Decoder for type: " + type);
} }
public void init(EndpointConfig config)
{
for (DecoderWrapper decoder : decoderMap.values())
{
decoder.getDecoder().init(config);
}
}
public Set<Class<?>> keySet() public Set<Class<?>> keySet()
{ {
return decoderMap.keySet(); return decoderMap.keySet();

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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 javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
/**
* Client {@link ContainerProvider} implementation
*/
public class JettyClientContainerProvider extends ContainerProvider
{
@Override
protected WebSocketContainer getContainer()
{
ClientContainer container = new ClientContainer();
container.start();
return container;
}
}

View File

@ -1,95 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.LinkedList;
import java.util.ServiceLoader;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class JettyContainerProvider extends ContainerProvider
{
private static final Logger LOG = Log.getLogger(JettyContainerProvider.class);
private final WebSocketContainer websocketContainer;
public JettyContainerProvider()
{
// Holds the list of ContainerService implementations found.
LinkedList<ContainerService> services = new LinkedList<ContainerService>();
for (ContainerService impl : ServiceLoader.load(ContainerService.class))
{
if (impl instanceof ClientContainer)
{
// Sort client impls last.
services.addLast(impl);
}
else
{
// All others first. (such as ServiceContainer impls)
services.addFirst(impl);
}
}
if (services.size() <= 0)
{
LOG.warn("Found no {} in classloader",ContainerService.class.getName());
websocketContainer = null;
return;
}
if (LOG.isDebugEnabled())
{
StringBuilder str = new StringBuilder();
int len = services.size();
str.append("Found ").append(len).append(" websocket container");
if (len > 1)
{
str.append('s');
}
for (int i = 0; i < len; i++)
{
ContainerService service = services.get(i);
str.append("\n [").append(i).append("] ").append(service.getClass().getName());
}
LOG.debug(str.toString());
}
// Use first one (in list)
ContainerService chosen = services.getFirst();
LOG.debug("Using WebSocketContainer: {}",chosen.getClass().getName());
chosen.start();
websocketContainer = chosen;
}
@Override
protected WebSocketContainer getContainer()
{
return websocketContainer;
}
}

View File

@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler; import javax.websocket.MessageHandler;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
@ -42,20 +43,21 @@ public class MessageHandlers
/** /**
* Factory for MessageHandlerMetadata instances. * Factory for MessageHandlerMetadata instances.
*/ */
private MessageHandlerMetadataFactory factory; private final MessageHandlerMetadataFactory factory;
/** /**
* Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()} * Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()}
*/ */
private final MessageHandlerWrapper wrappers[]; private final MessageHandlerWrapper wrappers[];
public MessageHandlers() public MessageHandlers(MessageHandlerMetadataFactory factory)
{ {
Objects.requireNonNull(factory,"MessageHandlerMetadataFactory cannot be null");
this.factory = factory;
this.wrappers = new MessageHandlerWrapper[MessageType.values().length]; this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
} }
public void add(MessageHandler handler) public void add(MessageHandler handler)
{ {
assertFactoryDefined();
Objects.requireNonNull(handler,"MessageHandler cannot be null"); Objects.requireNonNull(handler,"MessageHandler cannot be null");
synchronized (wrappers) synchronized (wrappers)
@ -89,19 +91,6 @@ public class MessageHandlers
} }
} }
private void assertFactoryDefined()
{
if (this.factory == null)
{
throw new IllegalStateException("MessageHandlerMetadataFactory has not been set");
}
}
public MessageHandlerMetadataFactory getFactory()
{
return factory;
}
public Set<MessageHandler> getUnmodifiableHandlerSet() public Set<MessageHandler> getUnmodifiableHandlerSet()
{ {
Set<MessageHandler> ret = new HashSet<>(); Set<MessageHandler> ret = new HashSet<>();
@ -127,8 +116,6 @@ public class MessageHandlers
public void remove(MessageHandler handler) public void remove(MessageHandler handler)
{ {
assertFactoryDefined();
try try
{ {
for (MessageHandlerMetadata metadata : factory.getMetadata(handler.getClass())) for (MessageHandlerMetadata metadata : factory.getMetadata(handler.getClass()))
@ -141,9 +128,4 @@ public class MessageHandlers
LOG.warn("Unable to identify MessageHandler: " + handler.getClass().getName(),e); LOG.warn("Unable to identify MessageHandler: " + handler.getClass().getName(),e);
} }
} }
public void setFactory(MessageHandlerMetadataFactory factory)
{
this.factory = factory;
}
} }

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import javax.websocket.OnMessage; import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException; import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role; import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@ -37,16 +38,21 @@ public class JsrParamIdBinary extends JsrParamIdOnMessage implements IJsrParamId
@Override @Override
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
{ {
Class<?> type = param.type; // Session parameter (optional)
if (param.type.isAssignableFrom(Session.class))
{
param.bind(Role.SESSION);
return true;
}
if (type.isAssignableFrom(ByteBuffer.class)) if (param.type.isAssignableFrom(ByteBuffer.class))
{ {
param.bind(Role.MESSAGE_BINARY); param.bind(Role.MESSAGE_BINARY);
callable.setDecoder(ByteBufferDecoder.INSTANCE); callable.setDecoder(ByteBufferDecoder.INSTANCE);
return true; return true;
} }
if (type.isAssignableFrom(byte[].class)) if (param.type.isAssignableFrom(byte[].class))
{ {
param.bind(Role.MESSAGE_BINARY); param.bind(Role.MESSAGE_BINARY);
callable.setDecoder(ByteArrayDecoder.INSTANCE); callable.setDecoder(ByteArrayDecoder.INSTANCE);
@ -54,7 +60,7 @@ public class JsrParamIdBinary extends JsrParamIdOnMessage implements IJsrParamId
} }
// Boolean (for indicating partial message support) // Boolean (for indicating partial message support)
if (type.isAssignableFrom(Boolean.TYPE)) if (param.type.isAssignableFrom(Boolean.TYPE))
{ {
param.bind(Role.MESSAGE_PARTIAL_FLAG); param.bind(Role.MESSAGE_PARTIAL_FLAG);
return true; return true;

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.websocket.jsr356.annotations; package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.PongMessage; import javax.websocket.PongMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException; import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role; import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@ -31,6 +32,13 @@ public class JsrParamIdPong extends JsrParamIdOnMessage implements IJsrParamId
@Override @Override
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
{ {
// Session parameter (optional)
if (param.type.isAssignableFrom(Session.class))
{
param.bind(Role.SESSION);
return true;
}
if (param.type.isAssignableFrom(PongMessage.class)) if (param.type.isAssignableFrom(PongMessage.class))
{ {
assertPartialMessageSupportDisabled(param,callable); assertPartialMessageSupportDisabled(param,callable);

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.websocket.jsr356.annotations; package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.OnMessage; import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException; import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role; import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@ -42,6 +43,13 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
@Override @Override
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
{ {
// Session parameter (optional)
if (param.type.isAssignableFrom(Session.class))
{
param.bind(Role.SESSION);
return true;
}
// String for whole message // String for whole message
if (param.type.isAssignableFrom(String.class)) if (param.type.isAssignableFrom(String.class))
{ {

View File

@ -26,12 +26,14 @@ import java.nio.ByteBuffer;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode; import javax.websocket.CloseReason.CloseCode;
import javax.websocket.CloseReason.CloseCodes; import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint; import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig; import javax.websocket.EndpointConfig;
import javax.websocket.Session; import javax.websocket.Session;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.CloseInfo;
@ -39,16 +41,23 @@ import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.AbstractEventDriver; import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriver; import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.MessageAppender; import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.jsr356.ContainerService;
import org.eclipse.jetty.websocket.jsr356.Decoders;
import org.eclipse.jetty.websocket.jsr356.JsrSession; import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.MessageHandlers;
import org.eclipse.jetty.websocket.jsr356.MessageType; import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryPartialMessage; import org.eclipse.jetty.websocket.jsr356.messages.BinaryPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryStreamMessage; import org.eclipse.jetty.websocket.jsr356.messages.BinaryStreamMessage;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryWholeMessage; import org.eclipse.jetty.websocket.jsr356.messages.BinaryWholeMessage;
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadataFactory;
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper; import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
import org.eclipse.jetty.websocket.jsr356.messages.TextPartialMessage; import org.eclipse.jetty.websocket.jsr356.messages.TextPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.TextStreamMessage; import org.eclipse.jetty.websocket.jsr356.messages.TextStreamMessage;
import org.eclipse.jetty.websocket.jsr356.messages.TextWholeMessage; import org.eclipse.jetty.websocket.jsr356.messages.TextWholeMessage;
/**
* EventDriver for websocket that extend from {@link javax.websocket.Endpoint}
*/
public class JsrEndpointEventDriver extends AbstractEventDriver implements EventDriver public class JsrEndpointEventDriver extends AbstractEventDriver implements EventDriver
{ {
private static final Logger LOG = Log.getLogger(JsrEndpointEventDriver.class); private static final Logger LOG = Log.getLogger(JsrEndpointEventDriver.class);
@ -82,6 +91,11 @@ public class JsrEndpointEventDriver extends AbstractEventDriver implements Event
if (activeMessage == null) if (activeMessage == null)
{ {
MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.BINARY); MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.BINARY);
if (wrapper == null)
{
LOG.debug("No BINARY MessageHandler declared");
return;
}
if (wrapper.wantsPartialMessages()) if (wrapper.wantsPartialMessages())
{ {
activeMessage = new BinaryPartialMessage(wrapper); activeMessage = new BinaryPartialMessage(wrapper);
@ -170,6 +184,11 @@ public class JsrEndpointEventDriver extends AbstractEventDriver implements Event
if (activeMessage == null) if (activeMessage == null)
{ {
MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.TEXT); MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.TEXT);
if (wrapper == null)
{
LOG.debug("No TEXT MessageHandler declared");
return;
}
if (wrapper.wantsPartialMessages()) if (wrapper.wantsPartialMessages())
{ {
activeMessage = new TextPartialMessage(wrapper); activeMessage = new TextPartialMessage(wrapper);
@ -204,14 +223,29 @@ public class JsrEndpointEventDriver extends AbstractEventDriver implements Event
{ {
// Cast should be safe, as it was created by JsrSessionFactory // Cast should be safe, as it was created by JsrSessionFactory
this.jsrsession = (JsrSession)session; this.jsrsession = (JsrSession)session;
// TODO: Create Decoders (Facade)
// TODO: Create MessageHandlers (Facade) try
{
// Create Decoders
ContainerService container = (ContainerService)jsrsession.getContainer();
Decoders decoders = new Decoders(container.getDecoderMetadataFactory(),endpointconfig);
jsrsession.setDecodersFacade(decoders);
// Create MessageHandlers
MessageHandlerMetadataFactory metadataFactory = new MessageHandlerMetadataFactory(decoders);
MessageHandlers messageHandlers = new MessageHandlers(metadataFactory);
jsrsession.setMessageHandlerFacade(messageHandlers);
}
catch (DeploymentException e)
{
throw new WebSocketException(e);
}
// Allow end-user socket to adjust configuration // Allow end-user socket to adjust configuration
super.openSession(session); super.openSession(session);
// TODO: Initialize Decoders // Initialize Decoders
// TODO: Initialize MessageHandlers jsrsession.getDecodersFacade().init(endpointconfig);
} }
public void setEndpointconfig(EndpointConfig endpointconfig) public void setEndpointconfig(EndpointConfig endpointconfig)

View File

@ -1 +1 @@
org.eclipse.jetty.websocket.jsr356.JettyContainerProvider org.eclipse.jetty.websocket.jsr356.JettyClientContainerProvider

View File

@ -27,6 +27,8 @@ import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig; import javax.websocket.EndpointConfig;
import javax.websocket.Session; import javax.websocket.Session;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Assert; import org.junit.Assert;
/** /**
@ -34,6 +36,7 @@ import org.junit.Assert;
*/ */
public class EndpointEchoClient extends Endpoint public class EndpointEchoClient extends Endpoint
{ {
private static final Logger LOG = Log.getLogger(EndpointEchoClient.class);
private Session session = null; private Session session = null;
private CloseReason close = null; private CloseReason close = null;
public EchoCaptureHandler textCapture = new EchoCaptureHandler(); public EchoCaptureHandler textCapture = new EchoCaptureHandler();
@ -46,6 +49,7 @@ public class EndpointEchoClient extends Endpoint
@Override @Override
public void onOpen(Session session, EndpointConfig config) public void onOpen(Session session, EndpointConfig config)
{ {
LOG.debug("onOpen({}, {})",session,config);
this.session = session; this.session = session;
Assert.assertThat("Session",session,notNullValue()); Assert.assertThat("Session",session,notNullValue());
Assert.assertThat("EndpointConfig",config,notNullValue()); Assert.assertThat("EndpointConfig",config,notNullValue());

View File

@ -50,8 +50,7 @@ public class MessageHandlersTest
@Test @Test
public void testGetBinaryHandler() throws DeploymentException public void testGetBinaryHandler() throws DeploymentException
{ {
MessageHandlers mhs = new MessageHandlers(); MessageHandlers mhs = new MessageHandlers(factory);
mhs.setFactory(factory);
mhs.add(new ByteBufferPartialHandler()); mhs.add(new ByteBufferPartialHandler());
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.BINARY); MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.BINARY);
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteBufferPartialHandler.class)); Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteBufferPartialHandler.class));
@ -60,8 +59,7 @@ public class MessageHandlersTest
@Test @Test
public void testGetBothHandler() throws DeploymentException public void testGetBothHandler() throws DeploymentException
{ {
MessageHandlers mhs = new MessageHandlers(); MessageHandlers mhs = new MessageHandlers(factory);
mhs.setFactory(factory);
mhs.add(new StringWholeHandler()); mhs.add(new StringWholeHandler());
mhs.add(new ByteArrayWholeHandler()); mhs.add(new ByteArrayWholeHandler());
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT); MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT);
@ -73,25 +71,16 @@ public class MessageHandlersTest
@Test @Test
public void testGetTextHandler() throws DeploymentException public void testGetTextHandler() throws DeploymentException
{ {
MessageHandlers mhs = new MessageHandlers(); MessageHandlers mhs = new MessageHandlers(factory);
mhs.setFactory(factory);
mhs.add(new StringWholeHandler()); mhs.add(new StringWholeHandler());
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT); MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT);
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class)); Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class));
} }
@Test(expected = IllegalStateException.class)
public void testNoFactoryAdd()
{
MessageHandlers mhs = new MessageHandlers();
mhs.add(new ByteBufferPartialHandler());
}
@Test @Test
public void testReplaceTextHandler() throws DeploymentException public void testReplaceTextHandler() throws DeploymentException
{ {
MessageHandlers mhs = new MessageHandlers(); MessageHandlers mhs = new MessageHandlers(factory);
mhs.setFactory(factory);
MessageHandler oldText = new StringWholeHandler(); MessageHandler oldText = new StringWholeHandler();
mhs.add(oldText); // add a TEXT handler mhs.add(oldText); // add a TEXT handler
mhs.add(new ByteArrayWholeHandler()); // add BINARY handler mhs.add(new ByteArrayWholeHandler()); // add BINARY handler

View File

@ -46,6 +46,36 @@ public class JettyServerEndpointConfig implements ServerEndpointConfig
private List<Extension> extensions; private List<Extension> extensions;
private Map<String, Object> userProperties; private Map<String, Object> userProperties;
public JettyServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
{
this(endpointClass,anno.value());
addAll(anno.decoders(),this.decoders);
addAll(anno.encoders(),this.encoders);
addAll(anno.subprotocols(),this.subprotocols);
// no extensions declared in annotation
// no userProperties in annotation
if (anno.configurator() == null)
{
this.configurator = BaseConfigurator.INSTANCE;
}
else
{
try
{
this.configurator = anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
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);
}
}
}
public JettyServerEndpointConfig(Class<?> endpointClass, String path) public JettyServerEndpointConfig(Class<?> endpointClass, String path)
{ {
this.endpointClass = endpointClass; this.endpointClass = endpointClass;
@ -95,36 +125,6 @@ public class JettyServerEndpointConfig implements ServerEndpointConfig
} }
} }
public JettyServerEndpointConfig(ServerEndpoint anno) throws DeploymentException
{
this(anno.getClass(),anno.value());
addAll(anno.decoders(),this.decoders);
addAll(anno.encoders(),this.encoders);
addAll(anno.subprotocols(),this.subprotocols);
// no extensions declared in annotation
// no userProperties in annotation
if (anno.configurator() == null)
{
this.configurator = BaseConfigurator.INSTANCE;
}
else
{
try
{
this.configurator = anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
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);
}
}
}
private <T> void addAll(T[] arr, List<T> lst) private <T> void addAll(T[] arr, List<T> lst)
{ {
if (arr == null) if (arr == null)
@ -184,4 +184,26 @@ public class JettyServerEndpointConfig implements ServerEndpointConfig
{ {
return userProperties; return userProperties;
} }
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("JettyServerEndpointConfig [endpointClass=");
builder.append(endpointClass);
builder.append(", path=");
builder.append(path);
builder.append(", configurator=");
builder.append(configurator);
builder.append(", decoders=");
builder.append(decoders);
builder.append(", encoders=");
builder.append(encoders);
builder.append(", subprotocols=");
builder.append(subprotocols);
builder.append(", extensions=");
builder.append(extensions);
builder.append("]");
return builder.toString();
}
} }

View File

@ -26,35 +26,56 @@ import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig; import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator; import javax.websocket.server.ServerEndpointConfig.Configurator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class JettyServerEndpointConfigurator extends Configurator public class JettyServerEndpointConfigurator extends Configurator
{ {
private static final Logger LOG = Log.getLogger(JettyServerEndpointConfigurator.class);
@Override @Override
public boolean checkOrigin(String originHeaderValue) public boolean checkOrigin(String originHeaderValue)
{ {
return super.checkOrigin(originHeaderValue); return true;
} }
@Override @Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException
{ {
return super.getEndpointInstance(endpointClass); LOG.debug(".getEndpointInstance({})",endpointClass);
try
{
return endpointClass.newInstance();
}
catch (IllegalAccessException e)
{
throw new InstantiationException(String.format("%s: %s",e.getClass().getName(),e.getMessage()));
}
} }
@Override @Override
public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested)
{ {
return super.getNegotiatedExtensions(installed,requested); /* do nothing */
return null;
} }
@Override @Override
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) public String getNegotiatedSubprotocol(List<String> supported, List<String> requested)
{ {
return super.getNegotiatedSubprotocol(supported,requested); for (String possible : requested)
{
if (supported.contains(possible))
{
return possible;
}
}
return null;
} }
@Override @Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
{ {
super.modifyHandshake(sec,request,response); /* do nothing */
} }
} }

View File

@ -76,9 +76,10 @@ public class JsrCreator implements WebSocketCreator
// create endpoint class // create endpoint class
try try
{ {
return config.getEndpointClass().newInstance(); Class<?> endpointClass = config.getEndpointClass();
return config.getConfigurator().getEndpointInstance(endpointClass);
} }
catch (InstantiationException | IllegalAccessException e) catch (InstantiationException e)
{ {
LOG.debug("Unable to create websocket: " + config.getEndpointClass().getName(),e); LOG.debug("Unable to create websocket: " + config.getEndpointClass().getName(),e);
return null; return null;

View File

@ -50,7 +50,7 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
} }
this.endpoint = anno; this.endpoint = anno;
this.config = new JettyServerEndpointConfig(anno); this.config = new JettyServerEndpointConfig(websocket,anno);
this.decoders = new Decoders(container.getDecoderMetadataFactory(),config); this.decoders = new Decoders(container.getDecoderMetadataFactory(),config);
} }

View File

@ -18,54 +18,37 @@
package org.eclipse.jetty.websocket.jsr356.server; package org.eclipse.jetty.websocket.jsr356.server;
import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.DispatcherType;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException; import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpointConfig; import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.ClientContainer; import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.JsrSessionFactory;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner; import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointImpl;
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec; import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator; import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter; import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
public class ServerContainer extends ClientContainer implements javax.websocket.server.ServerContainer public class ServerContainer extends ClientContainer implements javax.websocket.server.ServerContainer, WebSocketServerFactory.Listener
{ {
private static final Logger LOG = Log.getLogger(ServerContainer.class); public static ServerContainer get(WebAppContext context)
public static ServerContainer getInstance()
{ {
return (ServerContainer)ContainerProvider.getWebSocketContainer(); return (ServerContainer)context.getAttribute(WebSocketConfiguration.JAVAX_WEBSOCKET_SERVER_CONTAINER);
} }
private ConcurrentHashMap<Class<?>, JsrServerMetadata> endpointServerMetadataCache = new ConcurrentHashMap<>();
private MappedWebSocketCreator mappedCreator;
public ServerContainer() private final MappedWebSocketCreator mappedCreator;
private WebSocketServerFactory webSocketServletFactory;
private ConcurrentHashMap<Class<?>, JsrServerMetadata> endpointServerMetadataCache = new ConcurrentHashMap<>();
public ServerContainer(MappedWebSocketCreator creator)
{ {
super(); super();
WebAppContext webapp = WebAppContext.getCurrentWebAppContext(); this.mappedCreator = creator;
if (webapp != null)
{
WebSocketUpgradeFilter filter = new WebSocketUpgradeFilter();
FilterHolder fholder = new FilterHolder(filter);
fholder.setName("Jetty_WebSocketUpgradeFilter");
fholder.setDisplayName("WebSocket Upgrade Filter");
String pathSpec = "/*";
webapp.addFilter(fholder,pathSpec,EnumSet.of(DispatcherType.REQUEST));
LOG.debug("Adding {} mapped to {} to {}",filter,pathSpec,webapp);
mappedCreator = filter;
}
else
{
LOG.debug("No active WebAppContext detected");
}
} }
@Override @Override
@ -87,11 +70,6 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
mappedCreator.addMapping(new WebSocketPathSpec(config.getPath()),creator); mappedCreator.addMapping(new WebSocketPathSpec(config.getPath()),creator);
} }
public void addMappedCreator(MappedWebSocketCreator mappedCreator)
{
this.mappedCreator = mappedCreator;
}
public JsrServerMetadata getServerEndpointMetadata(Class<?> endpointClass) throws DeploymentException public JsrServerMetadata getServerEndpointMetadata(Class<?> endpointClass) throws DeploymentException
{ {
JsrServerMetadata basemetadata = endpointServerMetadataCache.get(endpointClass); JsrServerMetadata basemetadata = endpointServerMetadataCache.get(endpointClass);
@ -105,4 +83,25 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
return basemetadata; return basemetadata;
} }
public WebSocketServletFactory getWebSocketServletFactory()
{
return webSocketServletFactory;
}
@Override
public void onWebSocketServerFactoryStarted(WebSocketServerFactory factory)
{
this.webSocketServletFactory = factory;
EventDriverFactory eventDriverFactory = this.webSocketServletFactory.getEventDriverFactory();
eventDriverFactory.addImplementation(new JsrServerEndpointImpl(this));
eventDriverFactory.addImplementation(new JsrEndpointImpl());
this.webSocketServletFactory.setSessionFactory(new JsrSessionFactory(this));
}
@Override
public void onWebSocketServerFactoryStopped(WebSocketServerFactory factory)
{
/* do nothing */
}
} }

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.websocket.jsr356.server; package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException; import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpoint; import javax.websocket.server.ServerEndpoint;
@ -58,7 +57,7 @@ public class ServerEndpointAnnotation extends DiscoveredAnnotation
String path = annotation.value(); String path = annotation.value();
LOG.info("Got path: \"{}\"",path); LOG.info("Got path: \"{}\"",path);
ServerContainer container = (ServerContainer)ContainerProvider.getWebSocketContainer(); ServerContainer container = ServerContainer.get(_context);
try try
{ {

View File

@ -0,0 +1,71 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.EnumSet;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
/**
* WebSocket Server Configuration component
*/
public class WebSocketConfiguration extends AbstractConfiguration
{
public static final String JAVAX_WEBSOCKET_SERVER_CONTAINER = "javax.websocket.server.ServerContainer";
private static final Logger LOG = Log.getLogger(WebSocketConfiguration.class);
@Override
public void configure(WebAppContext context) throws Exception
{
WebSocketUpgradeFilter filter = new WebSocketUpgradeFilter();
FilterHolder fholder = new FilterHolder(filter);
fholder.setName("Jetty_WebSocketUpgradeFilter");
fholder.setDisplayName("WebSocket Upgrade Filter");
String pathSpec = "/*";
context.addFilter(fholder,pathSpec,EnumSet.of(DispatcherType.REQUEST));
LOG.debug("Adding {} mapped to {} to {}",filter,pathSpec,context);
ServerContainer container = new ServerContainer(filter);
filter.setWebSocketServerFactoryListener(container);
context.setAttribute(JAVAX_WEBSOCKET_SERVER_CONTAINER,container);
}
@Override
public void preConfigure(WebAppContext context) throws Exception
{
// Add the annotation scanning handlers (if annotation scanning enabled)
for (Configuration config : context.getConfigurations())
{
if (config instanceof AnnotationConfiguration)
{
AnnotationConfiguration annocfg = (AnnotationConfiguration)config;
annocfg.addDiscoverableAnnotationHandler(new ServerEndpointAnnotationHandler(context));
}
}
}
}

View File

@ -23,16 +23,8 @@ import java.util.Queue;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoSocket; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoSocket;
@ -61,27 +53,15 @@ public class BasicAnnotatedTest
URI uri = wsb.getServerBaseURI(); URI uri = wsb.getServerBaseURI();
WebAppContext webapp = wsb.createWebAppContext(); WebAppContext webapp = wsb.createWebAppContext();
AnnotationConfiguration annocfg = new AnnotationConfiguration();
annocfg.addDiscoverableAnnotationHandler(new ServerEndpointAnnotationHandler(webapp));
// @formatter:off
webapp.setConfigurations(new Configuration[] {
annocfg,
new WebXmlConfiguration(),
new WebInfConfiguration(),
new PlusConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration()});
// @formatter:on
wsb.deployWebapp(webapp); wsb.deployWebapp(webapp);
// wsb.dump(); wsb.dump();
WebSocketClient client = new WebSocketClient(); WebSocketClient client = new WebSocketClient();
try try
{ {
client.start(); client.start();
JettyEchoSocket clientEcho = new JettyEchoSocket(); JettyEchoSocket clientEcho = new JettyEchoSocket();
Future<Session> foo = client.connect(clientEcho,uri.resolve("/echo")); Future<Session> foo = client.connect(clientEcho,uri.resolve("echo"));
// wait for connect // wait for connect
foo.get(1,TimeUnit.SECONDS); foo.get(1,TimeUnit.SECONDS);
clientEcho.sendMessage("Hello World"); clientEcho.sendMessage("Hello World");

View File

@ -61,7 +61,6 @@ public class BasicEndpointTest
URI uri = wsb.getServerBaseURI(); URI uri = wsb.getServerBaseURI();
WebAppContext webapp = wsb.createWebAppContext(); WebAppContext webapp = wsb.createWebAppContext();
// default webapp configuration used (no annotation scanning)
wsb.deployWebapp(webapp); wsb.deployWebapp(webapp);
wsb.dump(); wsb.dump();

View File

@ -0,0 +1,39 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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 org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
public class DummyCreator implements MappedWebSocketCreator
{
@Override
public void addMapping(PathSpec spec, WebSocketCreator creator)
{
/* do nothing */
}
@Override
public PathMappings<WebSocketCreator> getMappings()
{
return null;
}
}

View File

@ -46,6 +46,7 @@ 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.BasicOpenSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicPongMessageSocket; 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.BasicTextMessageStringSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.StatelessTextMessageStringSocket;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -81,7 +82,7 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
} }
} }
private static ServerContainer container = new ServerContainer(); private static ServerContainer container = new ServerContainer(new DummyCreator());
@Parameters @Parameters
public static Collection<Case[]> data() throws Exception public static Collection<Case[]> data() throws Exception
@ -111,6 +112,7 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
Case.add(data, BasicErrorThrowableSessionSocket.class, fError, Throwable.class, Session.class); Case.add(data, BasicErrorThrowableSessionSocket.class, fError, Throwable.class, Session.class);
// -- Text Events // -- Text Events
Case.add(data, BasicTextMessageStringSocket.class, fText, String.class); Case.add(data, BasicTextMessageStringSocket.class, fText, String.class);
Case.add(data, StatelessTextMessageStringSocket.class, fText, Session.class, String.class);
// -- Binary Events // -- Binary Events
Case.add(data, BasicBinaryMessageByteBufferSocket.class, fBinary, ByteBuffer.class); Case.add(data, BasicBinaryMessageByteBufferSocket.class, fBinary, ByteBuffer.class);
// -- Pong Events // -- Pong Events

View File

@ -55,7 +55,7 @@ public class ServerAnnotatedEndpointScanner_InvalidSignaturesTest
{ {
private static final Logger LOG = Log.getLogger(ServerAnnotatedEndpointScanner_InvalidSignaturesTest.class); private static final Logger LOG = Log.getLogger(ServerAnnotatedEndpointScanner_InvalidSignaturesTest.class);
private static ServerContainer container = new ServerContainer(); private static ServerContainer container = new ServerContainer(new DummyCreator());
@Parameters @Parameters
public static Collection<Class<?>[]> data() public static Collection<Class<?>[]> data()

View File

@ -25,6 +25,9 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ContextHandlerCollection;
@ -36,7 +39,12 @@ import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
import org.junit.Assert; import org.junit.Assert;
/** /**
@ -98,6 +106,19 @@ public class WSServer
WebAppContext context = new WebAppContext(); WebAppContext context = new WebAppContext();
context.setContextPath(this.contextPath); context.setContextPath(this.contextPath);
context.setWar(this.contextDir.getAbsolutePath()); context.setWar(this.contextDir.getAbsolutePath());
// @formatter:off
context.setConfigurations(new Configuration[] {
new WebSocketConfiguration(),
new AnnotationConfiguration(),
new WebXmlConfiguration(),
new WebInfConfiguration(),
new PlusConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration()});
// @formatter:on
return context; return context;
} }
@ -146,7 +167,7 @@ public class WSServer
host = "localhost"; host = "localhost";
} }
int port = connector.getLocalPort(); int port = connector.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port)); serverUri = new URI(String.format("ws://%s:%d%s/",host,port,contextPath));
LOG.debug("Server started on {}",serverUri); LOG.debug("Server started on {}",serverUri);
} }

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.samples;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.websocket.jsr356.server.TrackingSocket;
@ServerEndpoint(value = "/stateless")
public class StatelessTextMessageStringSocket extends TrackingSocket
{
@OnMessage
public void onText(Session session, String message)
{
addEvent("onText(%s,%s)",session,message);
dataLatch.countDown();
}
}

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.server;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -62,8 +63,14 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
*/ */
public class WebSocketServerFactory extends ContainerLifeCycle implements WebSocketCreator, WebSocketServletFactory public class WebSocketServerFactory extends ContainerLifeCycle implements WebSocketCreator, WebSocketServletFactory
{ {
private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class); public static interface Listener
{
void onWebSocketServerFactoryStarted(WebSocketServerFactory factory);
void onWebSocketServerFactoryStopped(WebSocketServerFactory factory);
}
private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class);
private static final ThreadLocal<UpgradeContext> ACTIVE_CONTEXT = new ThreadLocal<>(); private static final ThreadLocal<UpgradeContext> ACTIVE_CONTEXT = new ThreadLocal<>();
public static UpgradeContext getActiveUpgradeContext() public static UpgradeContext getActiveUpgradeContext()
@ -81,15 +88,16 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
handshakes.put(HandshakeRFC6455.VERSION,new HandshakeRFC6455()); handshakes.put(HandshakeRFC6455.VERSION,new HandshakeRFC6455());
} }
private final Queue<WebSocketSession> sessions = new ConcurrentLinkedQueue<>();
/** /**
* Have the factory maintain 1 and only 1 scheduler. All connections share this scheduler. * Have the factory maintain 1 and only 1 scheduler. All connections share this scheduler.
*/ */
private final Scheduler scheduler = new ScheduledExecutorScheduler(); private final Scheduler scheduler = new ScheduledExecutorScheduler();
private final Queue<WebSocketSession> sessions = new ConcurrentLinkedQueue<>();
private final String supportedVersions; private final String supportedVersions;
private final WebSocketPolicy basePolicy; private final WebSocketPolicy basePolicy;
private final EventDriverFactory eventDriverFactory; private final EventDriverFactory eventDriverFactory;
private final WebSocketExtensionFactory extensionFactory; private final WebSocketExtensionFactory extensionFactory;
private List<WebSocketServerFactory.Listener> listeners = new ArrayList<>();
private SessionFactory sessionFactory; private SessionFactory sessionFactory;
private WebSocketCreator creator; private WebSocketCreator creator;
private List<Class<?>> registeredSocketClasses; private List<Class<?>> registeredSocketClasses;
@ -177,6 +185,11 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
return upgrade(sockreq,sockresp,driver); return upgrade(sockreq,sockresp,driver);
} }
public void addListener(WebSocketServerFactory.Listener listener)
{
listeners.add(listener);
}
@Override @Override
public void cleanup() public void cleanup()
{ {
@ -232,10 +245,24 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
} }
} }
@Override
protected void doStart() throws Exception
{
for (WebSocketServerFactory.Listener listener : listeners)
{
listener.onWebSocketServerFactoryStarted(this);
}
super.doStart();
}
@Override @Override
protected void doStop() throws Exception protected void doStop() throws Exception
{ {
closeAllConnections(); closeAllConnections();
for (WebSocketServerFactory.Listener listener : listeners)
{
listener.onWebSocketServerFactoryStopped(this);
}
super.doStop(); super.doStop();
} }
@ -256,6 +283,11 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
return extensionFactory; return extensionFactory;
} }
public Collection<WebSocketServerFactory.Listener> getListeners()
{
return listeners;
}
@Override @Override
public WebSocketPolicy getPolicy() public WebSocketPolicy getPolicy()
{ {
@ -336,17 +368,17 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
return protocols; return protocols;
} }
/*
* (non-Javadoc)
*
* @see org.eclipse.jetty.websocket.server.WebSocketServletFactory#register(java.lang.Class)
*/
@Override @Override
public void register(Class<?> websocketPojo) public void register(Class<?> websocketPojo)
{ {
registeredSocketClasses.add(websocketPojo); registeredSocketClasses.add(websocketPojo);
} }
public void removeListener(WebSocketServerFactory.Listener listener)
{
listeners.remove(listener);
}
public boolean sessionClosed(WebSocketSession session) public boolean sessionClosed(WebSocketSession session)
{ {
return isRunning() && sessions.remove(session); return isRunning() && sessions.remove(session);

View File

@ -41,7 +41,6 @@ import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource; import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
import org.eclipse.jetty.websocket.server.pathmap.PathSpec; import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator; import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
/** /**
* Inline Servlet Filter to capture WebSocket upgrade requests and perform path mappings to {@link WebSocketCreator} objects. * Inline Servlet Filter to capture WebSocket upgrade requests and perform path mappings to {@link WebSocketCreator} objects.
@ -51,7 +50,8 @@ public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, D
{ {
private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class); private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class);
private PathMappings<WebSocketCreator> pathmap = new PathMappings<>(); private PathMappings<WebSocketCreator> pathmap = new PathMappings<>();
private WebSocketServletFactory factory; private WebSocketServerFactory factory;
private WebSocketServerFactory.Listener listener;
@Override @Override
public void addMapping(PathSpec spec, WebSocketCreator creator) public void addMapping(PathSpec spec, WebSocketCreator creator)
@ -76,21 +76,26 @@ public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, D
return; return;
} }
if ((request instanceof HttpServletRequest) && (request instanceof HttpServletResponse)) LOG.debug("doFilter({})",request);
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse))
{ {
HttpServletRequest httpreq = (HttpServletRequest)request; HttpServletRequest httpreq = (HttpServletRequest)request;
HttpServletResponse httpresp = (HttpServletResponse)response; HttpServletResponse httpresp = (HttpServletResponse)response;
String target = httpreq.getPathInfo(); String target = httpreq.getServletPath();
LOG.debug("target = [{}]",target);
if (factory.isUpgradeRequest(httpreq,httpresp)) if (factory.isUpgradeRequest(httpreq,httpresp))
{ {
MappedResource<WebSocketCreator> resource = pathmap.getMatch(target); MappedResource<WebSocketCreator> resource = pathmap.getMatch(target);
if (resource == null) if (resource == null)
{ {
LOG.debug("WebSocket Upgrade on {} has no associated endpoint",target);
// no match. // no match.
httpresp.sendError(HttpServletResponse.SC_NOT_FOUND,"No websocket endpoint matching path: " + target); httpresp.sendError(HttpServletResponse.SC_NOT_FOUND,"No websocket endpoint matching path: " + target);
return; return;
} }
LOG.debug("WebSocket Upgrade detected on {} for endpoint {}",target,resource);
WebSocketCreator creator = resource.getResource(); WebSocketCreator creator = resource.getResource();
@ -171,7 +176,8 @@ public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, D
policy.setInputBufferSize(Integer.parseInt(max)); policy.setInputBufferSize(Integer.parseInt(max));
} }
factory = WebSocketServletFactory.Loader.create(policy); factory = new WebSocketServerFactory(policy);
factory.addListener(this.listener);
factory.init(); factory.init();
} }
catch (Exception x) catch (Exception x)
@ -180,6 +186,11 @@ public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, D
} }
} }
public void setWebSocketServerFactoryListener(WebSocketServerFactory.Listener listener)
{
this.listener = listener;
}
@Override @Override
public String toString() public String toString()
{ {