467246 - Support javax.websocket version 1.1

+ WIP
This commit is contained in:
Joakim Erdfelt 2016-02-12 08:51:16 -07:00
parent 8de4751c6f
commit 39f1d7332c
135 changed files with 4316 additions and 3374 deletions

View File

@ -20,14 +20,17 @@ package org.eclipse.jetty.websocket.jsr356;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.function.Function;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
@ -60,8 +63,6 @@ import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.client.SimpleEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.decoders.PrimitiveDecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.encoders.PrimitiveEncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
/**
@ -79,11 +80,13 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
private final DecoderFactory decoderFactory;
/** Tracking all primitive encoders for the container */
private final EncoderFactory encoderFactory;
/** Tracking for all declared Client endpoints */
private final Map<Class<?>, EndpointMetadata> endpointClientMetadataCache;
/** Tracking for all open Sessions */
private Set<Session> openSessions = new CopyOnWriteArraySet<>();
/** The jetty websocket client in use for this container */
private WebSocketClient client;
private List<Function<Object,EndpointConfig>> annotatedConfigFunctions = new ArrayList<>();
public ClientContainer()
{
// This constructor is used with Standalone JSR Client usage.
@ -97,18 +100,18 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
this.scopeDelegate = scope;
client = new WebSocketClient(scope, new SslContextFactory(trustAll));
client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy()));
client.setSessionFactory(new JsrSessionFactory(this));
addBean(client);
this.endpointClientMetadataCache = new ConcurrentHashMap<>();
annotatedConfigFunctions.add(new ClientEndpointConfigFunction());
this.decoderFactory = new DecoderFactory(this,PrimitiveDecoderMetadataSet.INSTANCE);
this.encoderFactory = new EncoderFactory(this,PrimitiveEncoderMetadataSet.INSTANCE);
ShutdownThread.register(this);
}
private Session connect(EndpointInstance instance, URI path) throws IOException
private Session connect(ConfiguredEndpoint instance, URI path) throws IOException
{
Objects.requireNonNull(instance,"EndpointInstance cannot be null");
Objects.requireNonNull(path,"Path cannot be null");
@ -161,28 +164,28 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
@Override
public Session connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig config, URI path) throws DeploymentException, IOException
{
EndpointInstance instance = newClientEndpointInstance(endpointClass,config);
ConfiguredEndpoint instance = newClientEndpointInstance(endpointClass,config);
return connect(instance,path);
}
@Override
public Session connectToServer(Class<?> annotatedEndpointClass, URI path) throws DeploymentException, IOException
{
EndpointInstance instance = newClientEndpointInstance(annotatedEndpointClass,null);
ConfiguredEndpoint instance = newClientEndpointInstance(annotatedEndpointClass,null);
return connect(instance,path);
}
@Override
public Session connectToServer(Endpoint endpoint, ClientEndpointConfig config, URI path) throws DeploymentException, IOException
{
EndpointInstance instance = newClientEndpointInstance(endpoint,config);
ConfiguredEndpoint instance = newClientEndpointInstance(endpoint,config);
return connect(instance,path);
}
@Override
public Session connectToServer(Object endpoint, URI path) throws DeploymentException, IOException
{
EndpointInstance instance = newClientEndpointInstance(endpoint,null);
ConfiguredEndpoint instance = newClientEndpointInstance(endpoint,null);
return connect(instance,path);
}
@ -339,7 +342,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
return scopeDelegate.getSslContextFactory();
}
private EndpointInstance newClientEndpointInstance(Class<?> endpointClass, ClientEndpointConfig config)
private ConfiguredEndpoint newClientEndpointInstance(Class<?> endpointClass, ClientEndpointConfig config)
{
try
{
@ -351,7 +354,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
}
}
public EndpointInstance newClientEndpointInstance(Object endpoint, ClientEndpointConfig config)
public ConfiguredEndpoint newClientEndpointInstance(Object endpoint, ClientEndpointConfig config)
{
EndpointMetadata metadata = getClientEndpointMetadata(endpoint.getClass(),config);
ClientEndpointConfig cec = config;
@ -366,7 +369,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
cec = new EmptyClientEndpointConfig();
}
}
return new EndpointInstance(endpoint,cec,metadata);
return new ConfiguredEndpoint(endpoint,cec,metadata);
}
@Override

View File

@ -16,29 +16,24 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
package org.eclipse.jetty.websocket.jsr356;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
/**
* Associate a JSR Endpoint with its optional {@link EndpointConfig}
*/
public class EndpointInstance
public class ConfiguredEndpoint
{
/** The instance of the Endpoint */
private final Object endpoint;
/** The instance specific configuration for the Endpoint */
/** The optional instance specific configuration for the Endpoint */
private final EndpointConfig config;
/** The metadata for this endpoint */
private final EndpointMetadata metadata;
public EndpointInstance(Object endpoint, EndpointConfig config, EndpointMetadata metadata)
public ConfiguredEndpoint(Object endpoint, EndpointConfig config)
{
this.endpoint = endpoint;
this.config = config;
this.metadata = metadata;
}
public EndpointConfig getConfig()
@ -50,9 +45,4 @@ public class EndpointInstance
{
return endpoint;
}
public EndpointMetadata getMetadata()
{
return metadata;
}
}

View File

@ -19,7 +19,11 @@
package org.eclipse.jetty.websocket.jsr356;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.ByteBuffer;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
@ -30,10 +34,21 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.Decoder;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Partial;
import javax.websocket.MessageHandler.Whole;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Async;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
@ -45,9 +60,19 @@ import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.jsr356.functions.JsrOnCloseFunction;
import org.eclipse.jetty.websocket.jsr356.functions.JsrOnErrorFunction;
import org.eclipse.jetty.websocket.jsr356.functions.JsrOnOpenFunction;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryArrayPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryBufferPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.JsrInputStreamMessage;
import org.eclipse.jetty.websocket.jsr356.messages.JsrReaderMessage;
import org.eclipse.jetty.websocket.jsr356.messages.TextPartialMessage;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
@ -60,46 +85,252 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
private final ClientContainer container;
private final String id;
private final EndpointConfig config;
private final EndpointMetadata metadata;
private final DecoderFactory decoderFactory;
private final EncoderFactory encoderFactory;
/** Factory for MessageHandlers */
private final MessageHandlerFactory messageHandlerFactory;
/** Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()} */
private final MessageHandlerWrapper wrappers[];
private Set<MessageHandler> messageHandlerSet;
private List<Extension> negotiatedExtensions;
private Map<String, String> pathParameters = new HashMap<>();
private JsrAsyncRemote asyncRemote;
private JsrBasicRemote basicRemote;
public JsrSession(ClientContainer container, String id, URI requestURI, EventDriver websocket, LogicalConnection connection)
public JsrSession(ClientContainer container, String id, URI requestURI, Object websocket, LogicalConnection connection)
{
super(container, requestURI, websocket, connection);
if (!(websocket instanceof AbstractJsrEventDriver))
{
throw new IllegalArgumentException("Cannot use, not a JSR WebSocket: " + websocket);
}
AbstractJsrEventDriver jsr = (AbstractJsrEventDriver)websocket;
this.config = jsr.getConfig();
this.metadata = jsr.getMetadata();
this.container = container;
ConfiguredEndpoint cendpoint = (ConfiguredEndpoint)websocket;
this.config = cendpoint.getConfig();
DecoderMetadataSet decoderSet = new DecoderMetadataSet();
EncoderMetadataSet encoderSet = new EncoderMetadataSet();
// TODO: figure out how to populare the decoderSet / encoderSet
this.id = id;
this.decoderFactory = new DecoderFactory(this,metadata.getDecoders(),container.getDecoderFactory());
this.encoderFactory = new EncoderFactory(this,metadata.getEncoders(),container.getEncoderFactory());
this.messageHandlerFactory = new MessageHandlerFactory();
this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
this.messageHandlerSet = new HashSet<>();
this.decoderFactory = new DecoderFactory(this,decoderSet,container.getDecoderFactory());
this.encoderFactory = new EncoderFactory(this,encoderSet,container.getEncoderFactory());
}
@Override
protected void discoverEndpointFunctions(Object obj)
{
if(obj instanceof ConfiguredEndpoint)
{
throw new IllegalArgumentException("JSR356 Implementation expects a " + ConfiguredEndpoint.class.getName() + " but got: " + obj.getClass().getName());
}
ConfiguredEndpoint cendpoint = (ConfiguredEndpoint) obj;
// Endpoint
Object websocket = cendpoint.getEndpoint();
if(websocket instanceof Endpoint)
{
Endpoint endpoint = (Endpoint)websocket;
onOpenFunction = (sess) -> {
endpoint.onOpen(this,config);
return null;
};
onCloseFunction = (closeinfo) -> {
CloseCode closeCode = CloseCodes.getCloseCode(closeinfo.getStatusCode());
CloseReason closeReason = new CloseReason(closeCode,closeinfo.getReason());
endpoint.onClose(this,closeReason);
return null;
};
onErrorFunction = (cause) -> {
endpoint.onError(this,cause);
return null;
};
}
// Annotations
Class<?> websocketClass = websocket.getClass();
ClientEndpoint clientEndpoint = websocketClass.getAnnotation(ClientEndpoint.class);
if(clientEndpoint != null)
{
Method onmethod = null;
// @OnOpen [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(websocketClass,OnOpen.class);
if(onmethod != null)
{
assertNotSet(onOpenFunction,"Open Handler",websocketClass,onmethod);
onOpenFunction = new JsrOnOpenFunction(this,websocket,onmethod);
}
// @OnClose [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(websocketClass,OnClose.class);
if(onmethod != null)
{
assertNotSet(onCloseFunction,"Close Handler",websocketClass,onmethod);
onCloseFunction = new JsrOnCloseFunction(this,websocket,onmethod);
}
// @OnError [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(websocketClass,OnError.class);
if(onmethod != null)
{
assertNotSet(onErrorFunction,"Error Handler",websocketClass,onmethod);
onErrorFunction = new JsrOnErrorFunction(this,websocket,onmethod);
}
// @OnMessage [0..2]
Method onmessages[] = ReflectUtils.findAnnotatedMethods(websocketClass,OnMessage.class);
if(onmessages != null && onmessages.length > 0)
{
for(Method method: onmessages)
{
// Text
// TextStream
// Binary
// BinaryStream
// Pong
}
}
}
}
@Override
public <T> void addMessageHandler(Class<T> clazz, Partial<T> handler)
{
Objects.requireNonNull(handler, "MessageHandler.Partial cannot be null");
if (LOG.isDebugEnabled())
{
LOG.debug("MessageHandler.Partial class: {}",handler.getClass());
}
// No decoders for Partial messages per JSR-356 (PFD1 spec)
if(String.class.isAssignableFrom(clazz))
{
@SuppressWarnings("unchecked")
Partial<String> strhandler = (Partial<String>)handler;
setMessageAppender(MessageType.TEXT, new TextPartialMessage(strhandler));
}
else if(ByteBuffer.class.isAssignableFrom(clazz))
{
@SuppressWarnings("unchecked")
Partial<ByteBuffer> bufhandler = (Partial<ByteBuffer>)handler;
setMessageAppender(MessageType.BINARY, new BinaryBufferPartialMessage(bufhandler));
}
else if(byte[].class.isAssignableFrom(clazz))
{
@SuppressWarnings("unchecked")
Partial<byte[]> arrhandler = (Partial<byte[]>)handler;
setMessageAppender(MessageType.BINARY, new BinaryArrayPartialMessage(arrhandler));
}
else
{
StringBuilder err = new StringBuilder();
err.append("Unsupported class type for MessageHandler.Partial (only supports <String>, <ByteBuffer>, or <byte[]>): ");
err.append(clazz.getName());
throw new IllegalArgumentException(err.toString());
}
}
@Override
public <T> void addMessageHandler(Class<T> clazz, Whole<T> handler)
{
Objects.requireNonNull(handler, "MessageHandler.Whole cannot be null");
if (LOG.isDebugEnabled())
{
LOG.debug("MessageHandler.Whole class: {}",handler.getClass());
}
// Determine Decoder
DecoderFactory.Wrapper decoderWrapper = decoderFactory.getWrapperFor(clazz);
if (decoderWrapper == null)
{
StringBuilder err = new StringBuilder();
err.append("Unable to find decoder for type <");
err.append(clazz.getName());
err.append("> used in <");
err.append(handler.getClass().getName());
err.append(">");
throw new IllegalStateException(err.toString());
}
if(decoderWrapper.getMetadata().isStreamed())
{
// Streaming
if(InputStream.class.isAssignableFrom(clazz))
{
// Whole Text Streaming
@SuppressWarnings("unchecked")
Whole<Object> streamhandler = (Whole<Object>)handler;
Decoder.BinaryStream<?> streamdecoder = (Decoder.BinaryStream<?>)decoderWrapper.getDecoder();
setMessageAppender(MessageType.TEXT,new JsrInputStreamMessage(streamhandler, streamdecoder, websocket, getExecutor()));
}
else if(Reader.class.isAssignableFrom(clazz))
{
// Whole Reader Streaming
@SuppressWarnings("unchecked")
Whole<Object> streamhandler = (Whole<Object>)handler;
Decoder.TextStream<?> streamdecoder = (Decoder.TextStream<?>)decoderWrapper.getDecoder();
setMessageAppender(MessageType.BINARY,new JsrReaderMessage(streamhandler, streamdecoder, websocket, getExecutor()));
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void addMessageHandler(MessageHandler handler) throws IllegalStateException
{
Objects.requireNonNull(handler, "MessageHandler cannot be null");
Class<? extends MessageHandler> handlerClass = handler.getClass();
if (MessageHandler.Whole.class.isAssignableFrom(handlerClass))
{
Class<?> onMessageClass = ReflectUtils.findGenericClassFor(handlerClass,MessageHandler.Whole.class);
addMessageHandler(onMessageClass,(Whole)handler);
}
if (MessageHandler.Partial.class.isAssignableFrom(handlerClass))
{
Class<?> onMessageClass = ReflectUtils.findGenericClassFor(handlerClass,MessageHandler.Partial.class);
addMessageHandler(onMessageClass,(Partial)handler);
}
}
private void setMessageAppender(MessageType type, MessageSink appender)
{
synchronized(messageAppenders)
{
MessageSink other = messageAppenders[type.ordinal()];
if (other != null)
{
StringBuilder err = new StringBuilder();
err.append("Encountered duplicate MessageHandler handling for ");
err.append(type.name()).append(" type messages. ");
err.append(wrapper.getMetadata().getObjectType().getName());
err.append(">, ").append(metadata.getHandlerClass().getName());
err.append("<");
err.append(metadata.getMessageClass().getName());
err.append("> and ");
err.append(other.getMetadata().getHandlerClass().getName());
err.append("<");
err.append(other.getMetadata().getMessageClass().getName());
err.append("> both implement this message type");
throw new IllegalStateException(err.toString());
}
}
}
private void addMessageAppender(Class<?> clazz, MessageHandler handler)
{
synchronized(messageAppenders)
{
// TODO Auto-generated method stub
}
}
private void addMessageHandlerWrapper(Class<?> msgClazz, MessageHandler handler) throws IllegalStateException
{
Objects.requireNonNull(handler, "MessageHandler cannot be null");
synchronized (wrappers)
{
for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(handler.getClass()))
for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(msgClazz))
{
DecoderFactory.Wrapper wrapper = decoderFactory.getWrapperFor(metadata.getMessageClass());
if (wrapper == null)
@ -218,11 +449,6 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
return getPolicy().getMaxTextMessageSize();
}
public MessageHandlerFactory getMessageHandlerFactory()
{
return messageHandlerFactory;
}
@Override
public Set<MessageHandler> getMessageHandlers()
{
@ -230,14 +456,6 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
return new HashSet<MessageHandler>(messageHandlerSet);
}
public MessageHandlerWrapper getMessageHandlerWrapper(MessageType type)
{
synchronized (wrappers)
{
return wrappers[type.ordinal()];
}
}
@Override
public List<Extension> getNegotiatedExtensions()
{
@ -330,6 +548,12 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
}
}
public MessageSink newMessageAppenderFor(MessageType text)
{
// TODO Auto-generated method stub
return null;
}
@Override
public void setMaxBinaryMessageBufferSize(int length)
{
@ -380,4 +604,5 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
// JSR 356 specification mandates default batch mode to be off.
return BatchMode.OFF;
}
}

View File

@ -20,36 +20,28 @@ package org.eclipse.jetty.websocket.jsr356;
import java.net.URI;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.SessionFactory;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
public class JsrSessionFactory implements SessionFactory
{
private static final Logger LOG = Log.getLogger(JsrSessionFactory.class);
private final ClientContainer container;
public JsrSessionFactory(ClientContainer container)
{
if(LOG.isDebugEnabled()) {
LOG.debug("Container: {}", container);
}
this.container = container;
}
@Override
public WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection)
public WebSocketSession createSession(URI requestURI, Object websocket, LogicalConnection connection)
{
return new JsrSession(container,connection.getId(),requestURI,websocket,connection);
}
@Override
public boolean supports(EventDriver websocket)
public boolean supports(Object websocket)
{
return (websocket instanceof AbstractJsrEventDriver);
return (websocket instanceof ConfiguredEndpoint);
}
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Whole;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -37,7 +38,7 @@ public class MessageHandlerFactory
{
private static final Logger LOG = Log.getLogger(MessageHandlerFactory.class);
/** Registered MessageHandlers at this level */
private Map<Class<? extends MessageHandler>, List<MessageHandlerMetadata>> registered;
private Map<MessageHandler, List<MessageHandlerMetadata>> registered;
public MessageHandlerFactory()
{
@ -59,6 +60,21 @@ public class MessageHandlerFactory
return register(handler);
}
public <T> List<MessageHandlerMetadata> getMetadata(Class<T> clazz, Whole<T> handler) throws IllegalStateException
{
if (LOG.isDebugEnabled())
{
LOG.debug("getMetadata({},{})",clazz,handler);
}
List<MessageHandlerMetadata> ret = registered.get(handler);
if (ret != null)
{
return ret;
}
return register(handler);
}
public List<MessageHandlerMetadata> register(Class<? extends MessageHandler> handler)
{
List<MessageHandlerMetadata> metadatas = new ArrayList<>();

View File

@ -31,8 +31,8 @@ import javax.websocket.OnOpen;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotationScanner;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
public class AnnotatedEndpointScanner<T extends Annotation, C extends EndpointConfig> extends AbstractMethodAnnotationScanner<AnnotatedEndpointMetadata<T, C>>

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.websocket.jsr356.annotations;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
/**
* JSR-356 Parameter Identification processing.

View File

@ -26,8 +26,8 @@ import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;

View File

@ -21,7 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**

View File

@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
import javax.websocket.OnMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.OnMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;

View File

@ -21,7 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.OnError;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.websocket.jsr356.annotations;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
public abstract class JsrParamIdOnMessage extends JsrParamIdBase implements IJsrParamId
{

View File

@ -21,7 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import javax.websocket.PongMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
public class JsrParamIdPong extends JsrParamIdOnMessage implements IJsrParamId

View File

@ -22,7 +22,7 @@ import java.io.Reader;
import javax.websocket.OnMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**

View File

@ -23,7 +23,7 @@ import java.lang.reflect.Method;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.jsr356.EncoderFactory;
import org.eclipse.jetty.websocket.jsr356.JsrSession;

View File

@ -26,9 +26,9 @@ import javax.websocket.OnMessage;
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.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
@ -39,12 +39,12 @@ public class JsrClientEndpointImpl implements EventDriverImpl
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy) throws DeploymentException
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
AnnotatedClientEndpointMetadata metadata = (AnnotatedClientEndpointMetadata)ei.getMetadata();
JsrEvents<ClientEndpoint, ClientEndpointConfig> events = new JsrEvents<>(metadata);
@ -88,12 +88,12 @@ public class JsrClientEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
ClientEndpoint anno = endpoint.getClass().getAnnotation(ClientEndpoint.class);

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.websocket.jsr356.endpoints;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
@ -31,21 +32,24 @@ import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
public abstract class AbstractJsrEventDriver extends AbstractEventDriver
{
protected final EndpointMetadata metadata;
protected final Executor executor;
protected final EndpointConfig config;
protected JsrSession jsrsession;
private boolean hasCloseBeenCalled = false;
public AbstractJsrEventDriver(WebSocketPolicy policy, EndpointInstance endpointInstance)
public AbstractJsrEventDriver(WebSocketPolicy policy, ConfiguredEndpoint endpointInstance, Executor executor)
{
super(policy,endpointInstance.getEndpoint());
this.config = endpointInstance.getConfig();
this.metadata = endpointInstance.getMetadata();
this.executor = executor;
}
public EndpointConfig getConfig()
@ -111,4 +115,9 @@ public abstract class AbstractJsrEventDriver extends AbstractEventDriver
}
public abstract void setPathParameters(Map<String, String> pathParameters);
public void dispatch(Runnable runnable)
{
executor.execute(runnable);
}
}

View File

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
@ -35,8 +36,9 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.message.MessageInputStream;
import org.eclipse.jetty.websocket.common.message.MessageReader;
import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
import org.eclipse.jetty.websocket.common.message.ByteArrayMessageSink;
import org.eclipse.jetty.websocket.common.message.StringMessageSink;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryPartialOnMessage;
@ -50,9 +52,9 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
private static final Logger LOG = Log.getLogger(JsrAnnotatedEventDriver.class);
private final JsrEvents<?, ?> events;
public JsrAnnotatedEventDriver(WebSocketPolicy policy, EndpointInstance endpointInstance, JsrEvents<?, ?> events)
public JsrAnnotatedEventDriver(WebSocketPolicy policy, ConfiguredEndpoint endpointInstance, JsrEvents<?, ?> events, Executor executor)
{
super(policy,endpointInstance);
super(policy,endpointInstance,executor);
this.events = events;
}
@ -97,7 +99,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
{
LOG.debug("Whole Binary Message");
}
activeMessage = new SimpleBinaryMessage(this);
activeMessage = new ByteArrayMessageSink(this);
}
}
}
@ -175,6 +177,12 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
}
}
@Override
public void onObject(Object obj)
{
// TODO Auto-generated method stub
}
@Override
protected void onClose(CloseReason closereason)
{
@ -311,7 +319,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
{
LOG.debug("Whole Text Message");
}
activeMessage = new SimpleTextMessage(this);
activeMessage = new StringMessageSink(this);
}
}
}

View File

@ -23,28 +23,20 @@ import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Whole;
import javax.websocket.PongMessage;
import org.eclipse.jetty.util.BufferUtil;
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.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.message.MessageInputStream;
import org.eclipse.jetty.websocket.common.message.MessageReader;
import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryWholeMessage;
import org.eclipse.jetty.websocket.jsr356.messages.TextPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.TextWholeMessage;
/**
* EventDriver for websocket that extend from {@link javax.websocket.Endpoint}
@ -56,9 +48,9 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
private final Endpoint endpoint;
private Map<String, String> pathParameters;
public JsrEndpointEventDriver(WebSocketPolicy policy, EndpointInstance endpointInstance)
public JsrEndpointEventDriver(WebSocketPolicy policy, Executor executor, ConfiguredEndpoint endpointInstance)
{
super(policy,endpointInstance);
super(policy,executor,endpointInstance);
this.endpoint = (Endpoint)endpointInstance.getEndpoint();
}
@ -73,8 +65,8 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
{
if (activeMessage == null)
{
final MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.BINARY);
if (wrapper == null)
activeMessage = jsrsession.newMessageAppenderFor(MessageType.BINARY);
if (activeMessage == null)
{
if (LOG.isDebugEnabled())
{
@ -82,6 +74,8 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
}
return;
}
/*
if (wrapper.wantsPartialMessages())
{
activeMessage = new BinaryPartialMessage(wrapper);
@ -105,6 +99,7 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
{
activeMessage = new BinaryWholeMessage(this,wrapper);
}
*/
}
activeMessage.appendFrame(buffer,fin);
@ -122,6 +117,12 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
/* Ignored, handled by BinaryWholeMessage */
}
@Override
public void onObject(Object o)
{
// TODO: deliver to message handler
}
@Override
protected void onClose(CloseReason closereason)
{
@ -176,8 +177,8 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
{
if (activeMessage == null)
{
final MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.TEXT);
if (wrapper == null)
activeMessage = jsrsession.newMessageAppenderFor(MessageType.TEXT);
if (activeMessage == null)
{
if (LOG.isDebugEnabled())
{
@ -185,30 +186,31 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
}
return;
}
if (wrapper.wantsPartialMessages())
{
activeMessage = new TextPartialMessage(wrapper);
}
else if (wrapper.wantsStreams())
{
final MessageReader stream = new MessageReader(new MessageInputStream());
activeMessage = stream;
dispatch(new Runnable()
{
@SuppressWarnings("unchecked")
@Override
public void run()
{
MessageHandler.Whole<Reader> handler = (Whole<Reader>)wrapper.getHandler();
handler.onMessage(stream);
}
});
}
else
{
activeMessage = new TextWholeMessage(this,wrapper);
}
// if (wrapper.wantsPartialMessages())
// {
// activeMessage = new TextPartialMessage(wrapper);
// }
// else if (wrapper.wantsStreams())
// {
// final MessageReader stream = new MessageReader(new MessageInputStream());
// activeMessage = stream;
//
// dispatch(new Runnable()
// {
// @SuppressWarnings("unchecked")
// @Override
// public void run()
// {
// MessageHandler.Whole<Reader> handler = (Whole<Reader>)wrapper.getHandler();
// handler.onMessage(stream);
// }
// });
// }
// else
// {
// activeMessage = new TextWholeMessage(this,wrapper);
// }
}
activeMessage.appendFrame(buffer,fin);
@ -240,8 +242,8 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
private void onPongMessage(ByteBuffer buffer)
{
final MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.PONG);
if (wrapper == null)
MessageSink appender = jsrsession.newMessageAppenderFor(MessageType.PONG);
if (appender == null)
{
if (LOG.isDebugEnabled())
{
@ -263,9 +265,14 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
BufferUtil.flipToFlush(pongBuf,0);
}
@SuppressWarnings("unchecked")
Whole<PongMessage> pongHandler = (Whole<PongMessage>)wrapper.getHandler();
pongHandler.onMessage(new JsrPongMessage(pongBuf));
try
{
appender.appendFrame(pongBuf,true);
}
catch (IOException e)
{
LOG.debug(e);
}
}
@Override

View File

@ -21,18 +21,19 @@ package org.eclipse.jetty.websocket.jsr356.endpoints;
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;
public class JsrEndpointImpl implements EventDriverImpl
{
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
return new JsrEndpointEventDriver(policy,(EndpointInstance)websocket);
return new JsrEndpointEventDriver(policy,(ConfiguredEndpoint)websocket);
}
@Override
@ -44,12 +45,12 @@ public class JsrEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
return (endpoint instanceof javax.websocket.Endpoint);

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.endpoints;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.client.JsrClientEndpointImpl;
public class JsrEventDriverFactory extends EventDriverFactory
@ -41,9 +42,9 @@ public class JsrEventDriverFactory extends EventDriverFactory
@Override
protected String getClassName(Object websocket)
{
if (websocket instanceof EndpointInstance)
if (websocket instanceof ConfiguredEndpoint)
{
EndpointInstance ce = (EndpointInstance)websocket;
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
return ce.getEndpoint().getClass().getName();
}

View File

@ -0,0 +1,101 @@
//
// ========================================================================
// 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.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* javax.websocket {@link OnMessage} method {@link Function} for BINARY/byte[] types
*/
public class JsrOnByteArrayFunction implements Function<byte[], Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int BUFFER = 2;
private static final int OFFSET = 3;
private static final int LENGTH = 4;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(byte[].class).indexedAs(BUFFER));
ARGBUILDER.addSignature(new ExactSignature(byte[].class,int.class,int.class).indexedAs(BUFFER,OFFSET,LENGTH));
ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class).indexedAs(SESSION,BUFFER));
ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class,int.class,int.class).indexedAs(SESSION,BUFFER,OFFSET,LENGTH));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnByteArrayFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,BUFFER,OFFSET,LENGTH);
}
@Override
public Void apply(byte[] bin)
{
Object args[] = this.callable.toArgs(session,bin,0,bin.length);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,98 @@
//
// ========================================================================
// 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.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.function.Function;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* javax.websocket {@link OnMessage} method {@link Function} for BINARY/{@link ByteBuffer} types
*/
public class JsrOnByteBufferFunction implements Function<ByteBuffer, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int BUFFER = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(ByteBuffer.class).indexedAs(BUFFER));
ARGBUILDER.addSignature(new ExactSignature(Session.class,ByteBuffer.class).indexedAs(SESSION,BUFFER));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnByteBufferFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,BUFFER);
}
@Override
public Void apply(ByteBuffer bin)
{
Object args[] = this.callable.toArgs(session,bin);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// 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.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnClose;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* javax.websocket {@link OnClose} method {@link Function}
*/
public class JsrOnCloseFunction implements Function<CloseInfo, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int STATUS_CODE = 2;
private static final int REASON = 3;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature().indexedAs());
ARGBUILDER.addSignature(new ExactSignature(Session.class).indexedAs(SESSION));
ARGBUILDER.addSignature(new ExactSignature(int.class,String.class).indexedAs(STATUS_CODE,REASON));
ARGBUILDER.addSignature(new ExactSignature(Session.class,int.class,String.class).indexedAs(SESSION,STATUS_CODE,REASON));
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnCloseFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnClose.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnClose.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,STATUS_CODE,REASON);
}
@Override
public Void apply(CloseInfo closeinfo)
{
Object args[] = this.callable.toArgs(session,closeinfo.getStatusCode(),closeinfo.getReason());
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call close method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,86 @@
//
// ========================================================================
// 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.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnError;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
/**
* javax.websocket {@link OnError} method {@link Function}
*/
public class JsrOnErrorFunction implements Function<Throwable, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int CAUSE = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addParams(Throwable.class).indexedAs(CAUSE);
ARGBUILDER.addParams(Session.class,Throwable.class).indexedAs(SESSION,CAUSE);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnErrorFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnError.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnError.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,CAUSE);
}
@Override
public Void apply(Throwable cause)
{
Object args[] = this.callable.toArgs(session,cause);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call error method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,99 @@
//
// ========================================================================
// 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.functions;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* javax.websocket {@link OnMessage} method {@link Function} for BINARY/{@link InputStream} streaming
* types
*/
public class JsrOnInputStreamFunction implements Function<InputStream, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int STREAM = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(InputStream.class).indexedAs(STREAM));
ARGBUILDER.addSignature(new ExactSignature(Session.class,InputStream.class).indexedAs(SESSION,STREAM));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnInputStreamFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,STREAM);
}
@Override
public Void apply(InputStream stream)
{
Object args[] = this.callable.toArgs(session,stream);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,87 @@
//
// ========================================================================
// 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.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* javax.websocket {@link OnOpen} method {@link Function}
*/
public class JsrOnOpenFunction implements Function<org.eclipse.jetty.websocket.api.Session, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature().indexedAs());
ARGBUILDER.addSignature(new ExactSignature(Session.class).indexedAs(SESSION));
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnOpenFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnOpen.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnOpen.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION);
}
@Override
public Void apply(org.eclipse.jetty.websocket.api.Session sess)
{
Object args[] = this.callable.toArgs(this.session);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,98 @@
//
// ========================================================================
// 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.functions;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
/**
* javax.websocket {@link OnMessage} method {@link Function} for TEXT/{@link Reader} streaming types
*/
public class JsrOnReaderFunction implements Function<Reader, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int STREAM = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(Reader.class).indexedAs(STREAM));
ARGBUILDER.addSignature(new ExactSignature(Session.class,Reader.class).indexedAs(SESSION,STREAM));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnReaderFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,STREAM);
}
@Override
public Void apply(Reader stream)
{
Object args[] = this.callable.toArgs(session,stream);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,97 @@
//
// ========================================================================
// 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.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* javax.websocket {@link OnMessage} method {@link Function} for TEXT/{@link String} types
*/
public class JsrOnTextFunction implements Function<String, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int TEXT = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(String.class).indexedAs(TEXT));
ARGBUILDER.addSignature(new ExactSignature(Session.class,String.class).indexedAs(SESSION,TEXT));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public JsrOnTextFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,TEXT);
}
@Override
public Void apply(String text)
{
Object args[] = this.callable.toArgs(session,text);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -16,55 +16,37 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.common.message;
package org.eclipse.jetty.websocket.jsr356.messages;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.websocket.MessageHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.MessageSink;
public class SimpleBinaryMessage implements MessageAppender
/**
* Partial BINARY MessageAppender for MessageHandler.Partial&lt;byte[]&gt; interface
*/
public class BinaryArrayPartialMessage implements MessageSink
{
private static final int BUFFER_SIZE = 65535;
private final EventDriver onEvent;
protected final ByteArrayOutputStream out;
private int size;
protected boolean finished;
private final MessageHandler.Partial<byte[]> partialHandler;
public SimpleBinaryMessage(EventDriver onEvent)
public BinaryArrayPartialMessage(MessageHandler.Partial<byte[]> handler)
{
this.onEvent = onEvent;
this.out = new ByteArrayOutputStream(BUFFER_SIZE);
finished = false;
this.partialHandler = handler;
}
@Override
public void appendFrame(ByteBuffer payload, boolean isLast) throws IOException
{
if (finished)
{
throw new IOException("Cannot append to finished buffer");
}
if (payload == null)
{
// empty payload is valid
return;
}
onEvent.getPolicy().assertValidBinaryMessageSize(size + payload.remaining());
size += payload.remaining();
BufferUtil.writeTo(payload,out);
partialHandler.onMessage(BufferUtil.toArray(payload),isLast);
}
@Override
public void messageComplete()
{
finished = true;
byte data[] = out.toByteArray();
onEvent.onBinaryMessage(data);
/* nothing to do here */
}
}

View File

@ -0,0 +1,52 @@
//
// ========================================================================
// 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.messages;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.websocket.MessageHandler;
import org.eclipse.jetty.websocket.common.message.MessageSink;
/**
* Partial BINARY MessageAppender for MessageHandler.Partial&lt;ByteBuffer&gt; interface
*/
public class BinaryBufferPartialMessage implements MessageSink
{
private final MessageHandler.Partial<ByteBuffer> partialHandler;
public BinaryBufferPartialMessage(MessageHandler.Partial<ByteBuffer> handler)
{
this.partialHandler = handler;
}
@Override
public void appendFrame(ByteBuffer payload, boolean isLast) throws IOException
{
// No decoders for Partial messages per JSR-356 (PFD1 spec)
partialHandler.onMessage(payload.slice(),isLast);
}
@Override
public void messageComplete()
{
/* nothing to do here */
}
}

View File

@ -1,82 +0,0 @@
//
// ========================================================================
// 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.messages;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Partial;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
/**
* Partial BINARY MessageAppender for MessageHandler.Partial interface
*/
public class BinaryPartialMessage implements MessageAppender
{
private final MessageHandlerWrapper msgWrapper;
private final MessageHandler.Partial<Object> partialHandler;
@SuppressWarnings("unchecked")
public BinaryPartialMessage(MessageHandlerWrapper wrapper)
{
this.msgWrapper = wrapper;
this.partialHandler = (Partial<Object>)wrapper.getHandler();
}
@Override
public void appendFrame(ByteBuffer payload, boolean isLast) throws IOException
{
// No decoders for Partial messages per JSR-356 (PFD1 spec)
// Supported Partial<> Type #1: ByteBuffer
if (msgWrapper.isMessageType(ByteBuffer.class))
{
partialHandler.onMessage(payload==null?BufferUtil.EMPTY_BUFFER:
payload.slice(),isLast);
return;
}
// Supported Partial<> Type #2: byte[]
if (msgWrapper.isMessageType(byte[].class))
{
partialHandler.onMessage(payload==null?new byte[0]:
BufferUtil.toArray(payload),isLast);
return;
}
StringBuilder err = new StringBuilder();
err.append(msgWrapper.getHandler().getClass());
err.append(" does not implement an expected ");
err.append(MessageHandler.Partial.class.getName());
err.append(" of type ");
err.append(ByteBuffer.class.getName());
err.append(" or byte[]");
throw new IllegalStateException(err.toString());
}
@Override
public void messageComplete()
{
/* nothing to do here */
}
}

View File

@ -24,13 +24,13 @@ import java.nio.ByteBuffer;
import javax.websocket.OnMessage;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
* Partial BINARY MessageAppender for &#064;{@link OnMessage} annotated methods
*/
public class BinaryPartialOnMessage implements MessageAppender
public class BinaryPartialOnMessage implements MessageSink
{
private final JsrAnnotatedEventDriver driver;
private boolean finished;

View File

@ -27,11 +27,11 @@ import javax.websocket.MessageHandler.Whole;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
import org.eclipse.jetty.websocket.common.message.ByteArrayMessageSink;
import org.eclipse.jetty.websocket.jsr356.DecoderFactory;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class BinaryWholeMessage extends SimpleBinaryMessage
public class BinaryWholeMessage extends ByteArrayMessageSink
{
private final MessageHandlerWrapper msgWrapper;
private final MessageHandler.Whole<Object> wholeHandler;

View File

@ -0,0 +1,90 @@
//
// ========================================================================
// 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.messages;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import javax.websocket.Decoder;
import javax.websocket.Decoder.BinaryStream;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.common.message.MessageInputStream;
/**
* Handling of InputStreams (Binary messages) via javax.websocket
*/
public class JsrInputStreamMessage implements MessageSink
{
private final EventDriver events;
private final Decoder.BinaryStream<?> decoder;
private final Executor executor;
private MessageInputStream stream = null;
public JsrInputStreamMessage(EventDriver events, BinaryStream<?> decoder, Executor executor)
{
this.decoder = decoder;
this.events = events;
this.executor = executor;
}
@Override
public void appendFrame(ByteBuffer framePayload, boolean fin) throws IOException
{
boolean first = (stream == null);
stream = new MessageInputStream();
stream.appendFrame(framePayload,fin);
if (first)
{
executor.execute(new Runnable()
{
@Override
public void run()
{
try
{
if (decoder != null)
{
Object o = decoder.decode(stream);
events.onObject(o);
}
else
{
events.onInputStream(stream);
}
}
catch (Throwable t)
{
events.onError(t);
}
}
});
}
}
@Override
public void messageComplete()
{
stream.messageComplete();
stream = null;
}
}

View File

@ -0,0 +1,88 @@
//
// ========================================================================
// 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.messages;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import javax.websocket.Decoder;
import javax.websocket.Decoder.TextStream;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.common.message.MessageInputStream;
import org.eclipse.jetty.websocket.common.message.MessageReader;
public class JsrReaderMessage implements MessageSink
{
private final EventDriver events;
private final Decoder.TextStream<?> decoder;
private final Executor executor;
private MessageReader stream = null;
public JsrReaderMessage(TextStream<?> decoder, EventDriver events, Executor executor)
{
this.decoder = decoder;
this.events = events;
this.executor = executor;
}
@Override
public void appendFrame(ByteBuffer framePayload, boolean fin) throws IOException
{
boolean first = (stream == null);
stream = new MessageReader(new MessageInputStream());
stream.appendFrame(framePayload,fin);
if (first)
{
executor.execute(new Runnable()
{
@Override
public void run()
{
try
{
if (decoder != null)
{
Object o = decoder.decode(stream);
events.onObject(o);
}
else
{
events.onReader(stream);
}
}
catch (Throwable t)
{
events.onError(t);
}
}
});
}
}
@Override
public void messageComplete()
{
stream.messageComplete();
stream = null;
}
}

View File

@ -24,25 +24,20 @@ import java.nio.ByteBuffer;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Partial;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.common.util.Utf8PartialBuilder;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
/**
* Partial TEXT MessageAppender for MessageHandler.Partial interface
*/
public class TextPartialMessage implements MessageAppender
public class TextPartialMessage implements MessageSink
{
@SuppressWarnings("unused")
private final MessageHandlerWrapper msgWrapper;
private final MessageHandler.Partial<String> partialHandler;
private final Utf8PartialBuilder utf8Partial;
@SuppressWarnings("unchecked")
public TextPartialMessage(MessageHandlerWrapper wrapper)
public TextPartialMessage(Partial<String> handler)
{
this.msgWrapper = wrapper;
this.partialHandler = (Partial<String>)wrapper.getHandler();
this.partialHandler = handler;
this.utf8Partial = new Utf8PartialBuilder();
}
@ -58,6 +53,6 @@ public class TextPartialMessage implements MessageAppender
@Override
public void messageComplete()
{
/* nothing to do here */
utf8Partial.reset();
}
}

View File

@ -23,14 +23,14 @@ import java.nio.ByteBuffer;
import javax.websocket.OnMessage;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.common.util.Utf8PartialBuilder;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
* Partial TEXT MessageAppender for &#064;{@link OnMessage} annotated methods
*/
public class TextPartialOnMessage implements MessageAppender
public class TextPartialOnMessage implements MessageSink
{
private final JsrAnnotatedEventDriver driver;
private final Utf8PartialBuilder utf8Partial;

View File

@ -25,11 +25,11 @@ import javax.websocket.MessageHandler.Whole;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
import org.eclipse.jetty.websocket.common.message.StringMessageSink;
import org.eclipse.jetty.websocket.jsr356.DecoderFactory;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class TextWholeMessage extends SimpleTextMessage
public class TextWholeMessage extends StringMessageSink
{
private final MessageHandlerWrapper msgWrapper;
private final MessageHandler.Whole<Object> wholeHandler;

View File

@ -24,7 +24,7 @@ import java.util.List;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.jsr356.MessageType;

View File

@ -24,7 +24,7 @@ import java.util.List;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.jsr356.MessageType;

View File

@ -32,7 +32,6 @@ import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.test.DummyConnection;
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.client.SimpleEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
import org.eclipse.jetty.websocket.jsr356.handlers.ByteArrayWholeHandler;
import org.eclipse.jetty.websocket.jsr356.handlers.ByteBufferPartialHandler;
@ -60,7 +59,7 @@ public class JsrSessionTest
SimpleEndpointMetadata metadata = new SimpleEndpointMetadata(websocket.getClass());
// Executor executor = null;
EndpointInstance ei = new EndpointInstance(websocket,config,metadata);
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket,config,metadata);
EventDriver driver = new JsrEndpointEventDriver(policy,ei);
DummyConnection connection = new DummyConnection();

View File

@ -34,7 +34,7 @@ import javax.websocket.OnOpen;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.client.AnnotatedClientEndpointMetadata;

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
import org.eclipse.jetty.websocket.jsr356.client.AnnotatedClientEndpointMetadata;
@ -109,7 +110,7 @@ public class OnCloseTest
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
ClientEndpointConfig config = metadata.getConfig();
TrackingSocket endpoint = (TrackingSocket)testcase.closeClass.newInstance();
EndpointInstance ei = new EndpointInstance(endpoint,config,metadata);
ConfiguredEndpoint ei = new ConfiguredEndpoint(endpoint,config,metadata);
JsrEvents<ClientEndpoint, ClientEndpointConfig> jsrevents = new JsrEvents<>(metadata);
EventDriver driver = new JsrAnnotatedEventDriver(policy,ei,jsrevents);

View File

@ -27,6 +27,5 @@ public class ByteBufferPartialHandler implements MessageHandler.Partial<ByteBuff
@Override
public void onMessage(ByteBuffer partialMessage, boolean last)
{
// TODO Auto-generated method stub
}
}

View File

@ -36,8 +36,8 @@ 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.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
@ -156,7 +156,16 @@ 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.
return new EndpointInstance(endpoint,config,metadata);
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);
}
catch (InstantiationException e)
{

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.server.PathParam;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
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;

View File

@ -25,9 +25,9 @@ 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.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
@ -38,12 +38,12 @@ public class JsrServerEndpointImpl implements EventDriverImpl
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy) throws Throwable
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
AnnotatedServerEndpointMetadata metadata = (AnnotatedServerEndpointMetadata)ei.getMetadata();
JsrEvents<ServerEndpoint, ServerEndpointConfig> events = new JsrEvents<>(metadata);
@ -96,12 +96,12 @@ public class JsrServerEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
ServerEndpoint anno = endpoint.getClass().getAnnotation(ServerEndpoint.class);

View File

@ -23,7 +23,7 @@ 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.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
public class JsrServerExtendsEndpointImpl implements EventDriverImpl
@ -31,12 +31,12 @@ public class JsrServerExtendsEndpointImpl implements EventDriverImpl
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
JsrEndpointEventDriver driver = new JsrEndpointEventDriver(policy, ei);
ServerEndpointConfig config = (ServerEndpointConfig)ei.getConfig();
@ -58,12 +58,12 @@ public class JsrServerExtendsEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof EndpointInstance))
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
return (endpoint instanceof javax.websocket.Endpoint);

View File

@ -36,9 +36,9 @@ 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.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
@ -64,7 +64,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
addBean(webSocketServerFactory);
}
public EndpointInstance newClientEndpointInstance(Object endpoint, ServerEndpointConfig config, String path)
public ConfiguredEndpoint newClientEndpointInstance(Object endpoint, ServerEndpointConfig config, String path)
{
EndpointMetadata metadata = getClientEndpointMetadata(endpoint.getClass(),config);
ServerEndpointConfig cec = config;
@ -79,7 +79,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
cec = new BasicServerEndpointConfig(this,endpoint.getClass(),path);
}
}
return new EndpointInstance(endpoint,cec,metadata);
return new ConfiguredEndpoint(endpoint,cec,metadata);
}
@Override

View File

@ -39,9 +39,9 @@ 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.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.server.samples.partial.PartialTrackingSocket;
import org.junit.Assert;
import org.junit.Rule;
@ -76,7 +76,7 @@ public class OnPartialTest
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(containerScope,endpoint,config);
AnnotatedEndpointScanner<ServerEndpoint, ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
scanner.scan();
EndpointInstance ei = new EndpointInstance(websocket,config,metadata);
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket,config,metadata);
EventDriver driver = driverImpl.create(ei,policy);
Assert.assertThat("EventDriver",driver,notNullValue());

View File

@ -35,7 +35,7 @@ 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.events.annotated.InvalidSignatureException;
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;

View File

@ -18,6 +18,9 @@
package org.eclipse.jetty.websocket.api;
import java.util.ArrayList;
import java.util.List;
/**
* Settings for WebSocket operations.
*/
@ -35,6 +38,13 @@ public class WebSocketPolicy
return new WebSocketPolicy(WebSocketBehavior.SERVER);
}
/* NOTE TO OTHER DEVELOPERS:
* If you change any of these default values,
* make sure you sync the values with
* org.eclipse.jetty.websocket.api.annotations.WebSocket
* annotation defaults
*/
/**
* The maximum size of a text message during parsing/generating.
* <p>
@ -76,14 +86,14 @@ public class WebSocketPolicy
* <p>
* Negative values indicate a disabled timeout.
*/
private long asyncWriteTimeout = 60000;
private long asyncWriteTimeout = 60_000;
/**
* The time in ms (milliseconds) that a websocket may be idle before closing.
* <p>
* Default: 300000 (ms)
*/
private long idleTimeout = 300000;
private long idleTimeout = 300_000;
/**
* The size of the input (read from network layer) buffer size.
@ -97,6 +107,13 @@ public class WebSocketPolicy
*/
private final WebSocketBehavior behavior;
public static interface PolicyUpdate
{
public void onPolicyUpdate(WebSocketPolicy policy);
}
private List<PolicyUpdate> listeners = new ArrayList<>();
public WebSocketPolicy(WebSocketBehavior behavior)
{
this.behavior = behavior;
@ -152,9 +169,28 @@ public class WebSocketPolicy
clone.maxBinaryMessageBufferSize = this.maxBinaryMessageBufferSize;
clone.inputBufferSize = this.inputBufferSize;
clone.asyncWriteTimeout = this.asyncWriteTimeout;
// clone.listeners.addAll(this.listeners);
return clone;
}
public void addListener(PolicyUpdate update)
{
this.listeners.add(update);
}
public void removeListener(PolicyUpdate update)
{
this.listeners.remove(update);
}
private void notifyOfUpdate()
{
for(PolicyUpdate update: listeners)
{
update.onPolicyUpdate(this);
}
}
/**
* The timeout in ms (milliseconds) for async write operations.
* <p>
@ -248,8 +284,11 @@ public class WebSocketPolicy
*/
public void setAsyncWriteTimeout(long ms)
{
boolean dirty = (this.asyncWriteTimeout != ms);
assertLessThan("AsyncWriteTimeout",ms,"IdleTimeout",idleTimeout);
this.asyncWriteTimeout = ms;
if (dirty)
notifyOfUpdate();
}
/**
@ -260,8 +299,11 @@ public class WebSocketPolicy
*/
public void setIdleTimeout(long ms)
{
boolean dirty = (this.idleTimeout != ms);
assertGreaterThan("IdleTimeout",ms,0);
this.idleTimeout = ms;
if (dirty)
notifyOfUpdate();
}
/**
@ -272,11 +314,14 @@ public class WebSocketPolicy
*/
public void setInputBufferSize(int size)
{
boolean dirty = (this.inputBufferSize != size);
assertGreaterThan("InputBufferSize",size,1);
assertLessThan("InputBufferSize",size,"MaxTextMessageBufferSize",maxTextMessageBufferSize);
assertLessThan("InputBufferSize",size,"MaxBinaryMessageBufferSize",maxBinaryMessageBufferSize);
this.inputBufferSize = size;
if(dirty)
notifyOfUpdate();
}
/**
@ -289,9 +334,12 @@ public class WebSocketPolicy
*/
public void setMaxBinaryMessageBufferSize(int size)
{
boolean dirty = (this.maxBinaryMessageBufferSize != size);
assertGreaterThan("MaxBinaryMessageBufferSize",size,1);
this.maxBinaryMessageBufferSize = size;
if(dirty)
notifyOfUpdate();
}
/**
@ -304,9 +352,12 @@ public class WebSocketPolicy
*/
public void setMaxBinaryMessageSize(int size)
{
boolean dirty = (this.maxBinaryMessageSize != size);
assertGreaterThan("MaxBinaryMessageSize",size,1);
this.maxBinaryMessageSize = size;
if(dirty)
notifyOfUpdate();
}
/**
@ -319,9 +370,12 @@ public class WebSocketPolicy
*/
public void setMaxTextMessageBufferSize(int size)
{
boolean dirty = (this.maxTextMessageBufferSize != size);
assertGreaterThan("MaxTextMessageBufferSize",size,1);
this.maxTextMessageBufferSize = size;
if(dirty)
notifyOfUpdate();
}
/**
@ -334,9 +388,12 @@ public class WebSocketPolicy
*/
public void setMaxTextMessageSize(int size)
{
boolean dirty = (this.maxTextMessageSize != size);
assertGreaterThan("MaxTextMessageSize",size,1);
this.maxTextMessageSize = size;
if(dirty)
notifyOfUpdate();
}
@Override
@ -344,14 +401,10 @@ public class WebSocketPolicy
{
StringBuilder builder = new StringBuilder();
builder.append("WebSocketPolicy@").append(Integer.toHexString(hashCode()));
builder.append("[behavior=").append(behavior);
builder.append(",maxTextMessageSize=").append(maxTextMessageSize);
builder.append(",maxTextMessageBufferSize=").append(maxTextMessageBufferSize);
builder.append(",maxBinaryMessageSize=").append(maxBinaryMessageSize);
builder.append(",maxBinaryMessageBufferSize=").append(maxBinaryMessageBufferSize);
builder.append(",asyncWriteTimeout=").append(asyncWriteTimeout);
builder.append("[").append(behavior);
builder.append(",textSize=").append(maxTextMessageSize);
builder.append(",binarySize=").append(maxBinaryMessageSize);
builder.append(",idleTimeout=").append(idleTimeout);
builder.append(",inputBufferSize=").append(inputBufferSize);
builder.append("]");
return builder.toString();
}

View File

@ -25,6 +25,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.StatusCode;
/**
* Tags a POJO as being a WebSocket class.
@ -35,13 +36,47 @@ import org.eclipse.jetty.websocket.api.BatchMode;
{ ElementType.TYPE })
public @interface WebSocket
{
int inputBufferSize() default -2;
/* NOTE TO OTHER DEVELOPERS:
* If you change any of these default values,
* make sure you sync the values with WebSocketPolicy
*/
int maxBinaryMessageSize() default -2;
/**
* The size of the buffer used to read from the network layer.
* <p>
* Default: 4096 (4 K)
*/
int inputBufferSize() default 4 * 1024;
int maxIdleTime() default -2;
/**
* The maximum size of a binary message during parsing/generating.
* <p>
* Binary messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
* <p>
* Default: 65536 (64 K)
*/
int maxBinaryMessageSize() default 64 * 1024;
int maxTextMessageSize() default -2;
/**
* The time in ms (milliseconds) that a websocket may be idle before closing.
* <p>
* Default: 300000 (ms)
*/
int maxIdleTime() default 300_000;
/**
* The maximum size of a text message during parsing/generating.
* <p>
* Text messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
* <p>
* Default: 65536 (64 K)
*/
int maxTextMessageSize() default 64 * 1024;
/**
* The output frame buffering mode.
* <p>
* Default: {@link BatchMode#AUTO}
*/
BatchMode batchMode() default BatchMode.AUTO;
}

View File

@ -55,8 +55,6 @@ import org.eclipse.jetty.websocket.client.masks.RandomMasker;
import org.eclipse.jetty.websocket.common.SessionFactory;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.WebSocketSessionFactory;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
@ -71,7 +69,6 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
private final SslContextFactory sslContextFactory;
private final WebSocketExtensionFactory extensionRegistry;
private boolean daemon = false;
private EventDriverFactory eventDriverFactory;
private SessionFactory sessionFactory;
private ByteBufferPool bufferPool;
private Executor executor;
@ -137,7 +134,10 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
this.extensionRegistry = new WebSocketExtensionFactory(this);
this.masker = new RandomMasker();
this.eventDriverFactory = new EventDriverFactory(policy);
addBean(this.executor);
addBean(this.sslContextFactory);
addBean(this.bufferPool);
}
public Future<Session> connect(Object websocket, URI toUri) throws IOException
@ -197,26 +197,8 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
initializeClient();
ConnectionManager manager = getConnectionManager();
// Setup Driver for user provided websocket
EventDriver driver = null;
if (websocket instanceof EventDriver)
{
// Use the EventDriver as-is
driver = (EventDriver)websocket;
}
else
{
// Wrap websocket with appropriate EventDriver
driver = eventDriverFactory.wrap(websocket);
}
if (driver == null)
{
throw new IllegalStateException("Unable to identify as websocket object: " + websocket.getClass().getName());
}
// Create the appropriate (physical vs virtual) connection task
ConnectPromise promise = manager.connect(this,driver,request);
ConnectPromise promise = manager.connect(this,request,websocket);
if (upgradeListener != null)
{
@ -337,11 +319,6 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
return cookieStore;
}
public EventDriverFactory getEventDriverFactory()
{
return eventDriverFactory;
}
public Executor getExecutor()
{
return executor;
@ -553,11 +530,6 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
this.dispatchIO = dispatchIO;
}
public void setEventDriverFactory(EventDriverFactory factory)
{
this.eventDriverFactory = factory;
}
public void setExecutor(Executor executor)
{
updateBean(this.executor,executor);

View File

@ -27,7 +27,6 @@ import org.eclipse.jetty.websocket.client.ClientUpgradeResponse;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.client.masks.Masker;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
/**
* Holder for the pending connect information.
@ -36,26 +35,29 @@ public abstract class ConnectPromise extends FuturePromise<Session> implements R
{
private static final Logger LOG = Log.getLogger(ConnectPromise.class);
private final WebSocketClient client;
private final EventDriver driver;
private final ClientUpgradeRequest request;
private final Object webSocketEndpoint;
private final Masker masker;
private UpgradeListener upgradeListener;
private ClientUpgradeResponse response;
private WebSocketSession session;
public ConnectPromise(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
public ConnectPromise(WebSocketClient client, ClientUpgradeRequest request, Object websocket)
{
this.client = client;
this.driver = driver;
this.request = request;
this.webSocketEndpoint = websocket;
this.masker = client.getMasker();
}
@Override
public void failed(Throwable cause)
{
if (session != null)
{
// Notify websocket of failure to connect
driver.onError(cause);
session.notifyError(cause);
}
// Notify promise/future of failure to connect
super.failed(cause);
@ -66,9 +68,9 @@ public abstract class ConnectPromise extends FuturePromise<Session> implements R
return client;
}
public EventDriver getDriver()
public Object getWebSocketEndpoint()
{
return this.driver;
return webSocketEndpoint;
}
public Masker getMasker()

View File

@ -30,7 +30,6 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.events.EventDriver;
/**
* Internal Connection/Client Manager used to track active clients, their physical vs virtual connection information, and provide some means to create new
@ -42,9 +41,9 @@ public class ConnectionManager extends ContainerLifeCycle
{
private SocketAddress bindAddress;
public PhysicalConnect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
public PhysicalConnect(WebSocketClient client, ClientUpgradeRequest request, Object websocket)
{
super(client,driver,request);
super(client,request,websocket);
this.bindAddress = client.getBindAddress();
}
@ -138,9 +137,9 @@ public class ConnectionManager extends ContainerLifeCycle
this.client = client;
}
public ConnectPromise connect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
public ConnectPromise connect(WebSocketClient client, ClientUpgradeRequest request, Object websocket)
{
return new PhysicalConnect(client,driver,request);
return new PhysicalConnect(client,request,websocket);
}
@Override

View File

@ -44,7 +44,6 @@ import org.eclipse.jetty.websocket.client.ClientUpgradeResponse;
import org.eclipse.jetty.websocket.common.AcceptHash;
import org.eclipse.jetty.websocket.common.SessionFactory;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.extensions.ExtensionStack;
import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser;
import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser.ParseException;
@ -307,8 +306,8 @@ public class UpgradeConnection extends AbstractConnection implements Connection.
EndPoint endp = getEndPoint();
Executor executor = getExecutor();
EventDriver websocket = connectPromise.getDriver();
WebSocketPolicy policy = websocket.getPolicy();
Object websocket = connectPromise.getWebSocketEndpoint();
WebSocketPolicy policy = connectPromise.getClient().getPolicy().clonePolicy();
WebSocketClientConnection connection = new WebSocketClientConnection(endp,executor,connectPromise,policy);

View File

@ -103,7 +103,6 @@ public class WebSocketClientSelectorManager extends SelectorManager
else
{
// Standard "ws://"
endPoint.setIdleTimeout(connectPromise.getDriver().getPolicy().getIdleTimeout());
return newUpgradeConnection(channel,endPoint,connectPromise);
}
}

View File

@ -0,0 +1,57 @@
//
// ========================================================================
// 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.common;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
@SuppressWarnings("serial")
public class DuplicateAnnotationException extends InvalidWebSocketException
{
public static DuplicateAnnotationException build(Class<?> pojo, Class<? extends Annotation> annoClass, Method... methods)
{
// Build big detailed exception to help the developer
StringBuilder err = new StringBuilder();
err.append("Duplicate @");
err.append(annoClass.getSimpleName());
err.append(" declarations in: ");
err.append(pojo.getName());
for (Method method : methods)
{
err.append(System.lineSeparator());
ReflectUtils.append(err,method);
}
return new DuplicateAnnotationException(err.toString());
}
public DuplicateAnnotationException(String message)
{
super(message);
}
public DuplicateAnnotationException(String message, Throwable cause)
{
super(message,cause);
}
}

View File

@ -16,18 +16,18 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.common.events.annotated;
package org.eclipse.jetty.websocket.common;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.events.ParamList;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
@SuppressWarnings("serial")
public class InvalidSignatureException extends InvalidWebSocketException
{
public static InvalidSignatureException build(Method method, Class<? extends Annotation> annoClass, ParamList... paramlists)
public static InvalidSignatureException build(Method method, Class<? extends Annotation> annoClass, DynamicArgs.Builder ... dynArgsBuilders)
{
// Build big detailed exception to help the developer
StringBuilder err = new StringBuilder();
@ -38,29 +38,13 @@ public class InvalidSignatureException extends InvalidWebSocketException
err.append("Acceptable method declarations for @");
err.append(annoClass.getSimpleName());
err.append(" are:");
for (ParamList validParams : paramlists)
for (DynamicArgs.Builder argsBuilder : dynArgsBuilders)
{
for (Class<?>[] params : validParams)
for (DynamicArgs.Signature signature : argsBuilder.getSignatures())
{
err.append(System.lineSeparator());
err.append("public void ").append(method.getName());
err.append('(');
boolean delim = false;
for (Class<?> type : params)
{
if (delim)
{
err.append(',');
}
err.append(' ');
err.append(type.getName());
if (type.isArray())
{
err.append("[]");
}
delim = true;
}
err.append(')');
signature.appendDescription(err);
}
}
return new InvalidSignatureException(err.toString());

View File

@ -20,14 +20,12 @@ package org.eclipse.jetty.websocket.common;
import java.net.URI;
import org.eclipse.jetty.websocket.common.events.EventDriver;
/**
* Interface for creating jetty {@link WebSocketSession} objects.
*/
public interface SessionFactory
{
public boolean supports(EventDriver websocket);
public boolean supports(Object websocket);
public WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection);
public WebSocketSession createSession(URI requestURI, Object websocket, LogicalConnection connection);
}

View File

@ -19,8 +19,10 @@
package org.eclipse.jetty.websocket.common;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -28,9 +30,12 @@ import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.concurrent.Executor;
import java.util.function.Function;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@ -41,6 +46,7 @@ import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.CloseStatus;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
@ -48,17 +54,47 @@ import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketFrameListener;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
import org.eclipse.jetty.websocket.api.WebSocketPingPongListener;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.frames.CloseFrame;
import org.eclipse.jetty.websocket.common.frames.ReadOnlyDelegatedFrame;
import org.eclipse.jetty.websocket.common.functions.OnByteArrayFunction;
import org.eclipse.jetty.websocket.common.functions.OnByteBufferFunction;
import org.eclipse.jetty.websocket.common.functions.OnCloseFunction;
import org.eclipse.jetty.websocket.common.functions.OnErrorFunction;
import org.eclipse.jetty.websocket.common.functions.OnFrameFunction;
import org.eclipse.jetty.websocket.common.functions.OnInputStreamFunction;
import org.eclipse.jetty.websocket.common.functions.OnOpenFunction;
import org.eclipse.jetty.websocket.common.functions.OnReaderFunction;
import org.eclipse.jetty.websocket.common.functions.OnTextFunction;
import org.eclipse.jetty.websocket.common.io.IOState;
import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
import org.eclipse.jetty.websocket.common.message.ByteArrayMessageSink;
import org.eclipse.jetty.websocket.common.message.ByteBufferMessageSink;
import org.eclipse.jetty.websocket.common.message.InputStreamMessageSink;
import org.eclipse.jetty.websocket.common.message.MessageSink;
import org.eclipse.jetty.websocket.common.message.PartialBinaryMessageSink;
import org.eclipse.jetty.websocket.common.message.PartialTextMessageSink;
import org.eclipse.jetty.websocket.common.message.ReaderMessageSink;
import org.eclipse.jetty.websocket.common.message.StringMessageSink;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
@ManagedObject("A Jetty WebSocket Session")
public class WebSocketSession extends ContainerLifeCycle implements Session, RemoteEndpointFactory, WebSocketSessionScope, IncomingFrames, Connection.Listener, ConnectionStateListener
@ -68,21 +104,36 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
private final WebSocketContainerScope containerScope;
private final URI requestURI;
private final LogicalConnection connection;
private final EventDriver websocket;
private final Executor executor;
// The websocket endpoint object itself
private final Object endpoint;
// The functions for calling into websocket endpoint's declared event handlers
protected Function<Session, Void> onOpenFunction;
protected Function<CloseInfo, Void> onCloseFunction;
protected Function<Throwable, Void> onErrorFunction;
protected Function<ByteBuffer, Void> onPingFunction;
protected Function<ByteBuffer, Void> onPongFunction;
protected Function<Frame, Void> onFrameFunction;
// Message Handling sinks
protected MessageSink onTextSink;
protected MessageSink onBinarySink;
protected MessageSink activeMessageSink;
private ClassLoader classLoader;
private ExtensionFactory extensionFactory;
private RemoteEndpointFactory remoteEndpointFactory;
private BatchMode batchmode = BatchMode.AUTO;
private String protocolVersion;
private Map<String, String[]> parameterMap = new HashMap<>();
private RemoteEndpoint remote;
private IncomingFrames incomingHandler;
private WebSocketRemoteEndpoint remote;
private OutgoingFrames outgoingHandler;
private WebSocketPolicy policy;
private UpgradeRequest upgradeRequest;
private UpgradeResponse upgradeResponse;
public WebSocketSession(WebSocketContainerScope containerScope, URI requestURI, EventDriver websocket, LogicalConnection connection)
public WebSocketSession(WebSocketContainerScope containerScope, URI requestURI, Object endpoint, LogicalConnection connection)
{
Objects.requireNonNull(containerScope,"Container Scope cannot be null");
Objects.requireNonNull(requestURI,"Request URI cannot be null");
@ -90,23 +141,222 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
this.classLoader = Thread.currentThread().getContextClassLoader();
this.containerScope = containerScope;
this.requestURI = requestURI;
this.websocket = websocket;
this.endpoint = endpoint;
this.connection = connection;
this.executor = connection.getExecutor();
this.outgoingHandler = connection;
this.incomingHandler = websocket;
this.connection.getIOState().addListener(this);
this.policy = containerScope.getPolicy();
this.policy = containerScope.getPolicy().clonePolicy();
discoverEndpointFunctions(this.endpoint);
addBean(this.connection);
addBean(this.websocket);
}
protected void discoverEndpointFunctions(Object endpoint)
{
// Connection Listener
if (endpoint instanceof WebSocketConnectionListener)
{
WebSocketConnectionListener wslistener = (WebSocketConnectionListener)endpoint;
onOpenFunction = (sess) -> {
wslistener.onWebSocketConnect(sess);
return null;
};
onCloseFunction = (closeinfo) -> {
wslistener.onWebSocketClose(closeinfo.getStatusCode(),closeinfo.getReason());
return null;
};
onErrorFunction = (cause) -> {
wslistener.onWebSocketError(cause);
return null;
};
}
// Simple Data Listener
if (endpoint instanceof WebSocketListener)
{
WebSocketListener wslistener = (WebSocketListener)endpoint;
onTextSink = new StringMessageSink(policy,(payload) -> {
wslistener.onWebSocketText(payload);
return null;
});
onBinarySink = new ByteArrayMessageSink(policy,(payload) -> {
wslistener.onWebSocketBinary(payload,0,payload.length);
return null;
});
}
// Ping/Pong Listener
if (endpoint instanceof WebSocketPingPongListener)
{
WebSocketPingPongListener wslistener = (WebSocketPingPongListener)endpoint;
onPongFunction = (pong) -> {
ByteBuffer payload = pong;
if (pong == null)
payload = BufferUtil.EMPTY_BUFFER;
wslistener.onWebSocketPong(payload);
return null;
};
onPingFunction = (ping) -> {
ByteBuffer payload = ping;
if (ping == null)
payload = BufferUtil.EMPTY_BUFFER;
wslistener.onWebSocketPing(payload);
return null;
};
}
// Partial Data / Message Listener
if (endpoint instanceof WebSocketPartialListener)
{
for(Method method: WebSocketPartialListener.class.getDeclaredMethods())
{
if(method.getName().equals("onWebSocketPartialText"))
assertNotSet(onTextSink, "TEXT Message Handler", endpoint.getClass(), method);
else if(method.getName().equals("onWebSocketPartialBinary"))
assertNotSet(onBinarySink, "BINARY Message Handler", endpoint.getClass(), method);
}
WebSocketPartialListener wslistener = (WebSocketPartialListener)endpoint;
onTextSink = new PartialTextMessageSink((partial) -> {
wslistener.onWebSocketPartialText(partial.getPayload(),partial.isFin());
return null;
});
onBinarySink = new PartialBinaryMessageSink((partial) -> {
wslistener.onWebSocketPartialBinary(partial.getPayload(),partial.isFin());
return null;
});
}
// Frame Listener
if (endpoint instanceof WebSocketFrameListener)
{
WebSocketFrameListener wslistener = (WebSocketFrameListener)endpoint;
onFrameFunction = (frame) -> {
wslistener.onWebSocketFrame(new ReadOnlyDelegatedFrame(frame));
return null;
};
}
// Test for annotated websocket endpoint
Class<?> endpointClass = endpoint.getClass();
WebSocket websocket = endpointClass.getAnnotation(WebSocket.class);
if (websocket != null)
{
policy.setInputBufferSize(websocket.inputBufferSize());
policy.setMaxBinaryMessageSize(websocket.maxBinaryMessageSize());
policy.setMaxTextMessageSize(websocket.maxTextMessageSize());
policy.setIdleTimeout(websocket.maxIdleTime());
this.batchmode = websocket.batchMode();
Method onmethod = null;
// OnWebSocketConnect [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketConnect.class);
if (onmethod != null)
{
assertNotSet(onOpenFunction, "Open/Connect Handler", endpointClass, onmethod);
onOpenFunction = new OnOpenFunction(endpoint,onmethod);
}
// OnWebSocketClose [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketClose.class);
if (onmethod != null)
{
assertNotSet(onCloseFunction, "Close Handler", endpointClass, onmethod);
onCloseFunction = new OnCloseFunction(this,endpoint,onmethod);
}
// OnWebSocketError [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketError.class);
if (onmethod != null)
{
assertNotSet(onErrorFunction, "Error Handler", endpointClass, onmethod);
onErrorFunction = new OnErrorFunction(this,endpoint,onmethod);
}
// OnWebSocketFrame [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketFrame.class);
if (onmethod != null)
{
assertNotSet(onFrameFunction, "Frame Handler", endpointClass, onmethod);
onFrameFunction = new OnFrameFunction(this,endpoint,onmethod);
}
// OnWebSocketMessage [0..2]
Method onmessages[] = ReflectUtils.findAnnotatedMethods(endpointClass,OnWebSocketMessage.class);
if (onmessages != null && onmessages.length > 0)
{
for (Method onmsg : onmessages)
{
if (OnTextFunction.hasMatchingSignature(onmsg))
{
assertNotSet(onTextSink, "TEXT Message Handler", endpointClass, onmsg);
// Normal Text Message
onTextSink = new StringMessageSink(policy,new OnTextFunction(this,endpointClass,onmsg));
}
else if (OnByteBufferFunction.hasMatchingSignature(onmsg))
{
assertNotSet(onBinarySink, "Binary Message Handler", endpointClass, onmsg);
// ByteBuffer Binary Message
onBinarySink = new ByteBufferMessageSink(policy,new OnByteBufferFunction(this,endpointClass,onmsg));
}
else if (OnByteArrayFunction.hasMatchingSignature(onmsg))
{
assertNotSet(onBinarySink, "Binary Message Handler", endpointClass, onmsg);
// byte[] Binary Message
onBinarySink = new ByteArrayMessageSink(policy,new OnByteArrayFunction(this,endpointClass,onmsg));
}
else if (OnInputStreamFunction.hasMatchingSignature(onmsg))
{
assertNotSet(onBinarySink, "Binary Message Handler", endpointClass, onmsg);
// InputStream Binary Message
onBinarySink = new InputStreamMessageSink(executor,new OnInputStreamFunction(this,endpointClass,onmsg));
}
else if (OnReaderFunction.hasMatchingSignature(onmsg))
{
assertNotSet(onTextSink, "TEXT Message Handler", endpointClass, onmsg);
// Reader Text Message
onTextSink = new ReaderMessageSink(executor,new OnReaderFunction(this,endpointClass,onmsg));
}
else
{
// Not a valid @OnWebSocketMessage declaration signature
throw InvalidSignatureException.build(onmsg,OnWebSocketMessage.class,
OnTextFunction.getDynamicArgsBuilder(),
OnByteBufferFunction.getDynamicArgsBuilder(),
OnByteArrayFunction.getDynamicArgsBuilder(),
OnInputStreamFunction.getDynamicArgsBuilder(),
OnReaderFunction.getDynamicArgsBuilder());
}
}
}
}
}
protected void assertNotSet(Object val, String role, Class<?> pojo, Method method)
{
if(val == null)
return;
StringBuilder err = new StringBuilder();
err.append("Cannot replace previously assigned ");
err.append(role);
err.append(" with ");
ReflectUtils.append(err,pojo,method);
throw new InvalidWebSocketException(err.toString());
}
@Override
public void close()
{
/* This is assumed to always be a NORMAL closure, no reason phrase */
close(StatusCode.NORMAL, null);
connection.close(StatusCode.NORMAL,null);
}
@Override
@ -162,7 +412,8 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
{
if (LOG.isDebugEnabled())
LOG.debug("stopping - {}",this);
try
if (getConnection() != null)
{
close(StatusCode.SHUTDOWN,"Shutdown");
}
@ -177,16 +428,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
public void dump(Appendable out, String indent) throws IOException
{
dumpThis(out);
out.append(indent).append(" +- incomingHandler : ");
if (incomingHandler instanceof Dumpable)
{
((Dumpable)incomingHandler).dump(out,indent + " ");
}
else
{
out.append(incomingHandler.toString()).append(System.lineSeparator());
}
out.append(indent).append(" +- endpoint : ").append(endpoint.getClass().getName()).append('@').append(Integer.toHexString(endpoint.hashCode()));
out.append(indent).append(" +- outgoingHandler : ");
if (outgoingHandler instanceof Dumpable)
{
@ -249,6 +491,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
return this.containerScope;
}
public Executor getExecutor()
{
return executor;
}
public ExtensionFactory getExtensionFactory()
{
return extensionFactory;
@ -263,12 +510,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
return connection.getMaxIdleTimeout();
}
@ManagedAttribute(readonly = true)
public IncomingFrames getIncomingHandler()
{
return incomingHandler;
}
@Override
public InetSocketAddress getLocalAddress()
{
@ -331,7 +572,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
return this.upgradeResponse;
}
@Override
public WebSocketSession getWebSocketSession()
{
@ -352,12 +592,10 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
*/
@Override
public void incomingError(Throwable t)
{
if (connection.getIOState().isInputAvailable())
{
// Forward Errors to User WebSocket Object
websocket.incomingError(t);
}
if (onErrorFunction != null)
onErrorFunction.apply(t);
}
/**
@ -372,12 +610,120 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
Thread.currentThread().setContextClassLoader(classLoader);
if (connection.getIOState().isInputAvailable())
{
// Forward Frames Through Extension List
incomingHandler.incomingFrame(frame);
if (onFrameFunction != null)
onFrameFunction.apply(frame);
byte opcode = frame.getOpCode();
switch (opcode)
{
case OpCode.CLOSE:
{
boolean validate = true;
CloseFrame closeframe = (CloseFrame)frame;
CloseInfo close = new CloseInfo(closeframe,validate);
// process handshake
getConnection().getIOState().onCloseRemote(close);
return;
}
case OpCode.PING:
{
if (LOG.isDebugEnabled())
LOG.debug("PING: {}",BufferUtil.toDetailString(frame.getPayload()));
ByteBuffer pongBuf;
if (frame.hasPayload())
{
pongBuf = ByteBuffer.allocate(frame.getPayload().remaining());
BufferUtil.put(frame.getPayload().slice(),pongBuf);
BufferUtil.flipToFlush(pongBuf,0);
}
else
{
pongBuf = ByteBuffer.allocate(0);
}
if (onPingFunction != null)
onPingFunction.apply(frame.getPayload());
getRemote().sendPong(pongBuf);
break;
}
case OpCode.PONG:
{
if (LOG.isDebugEnabled())
LOG.debug("PONG: {}",BufferUtil.toDetailString(frame.getPayload()));
if (onPongFunction != null)
onPongFunction.apply(frame.getPayload());
break;
}
case OpCode.BINARY:
{
if (activeMessageSink == null)
activeMessageSink = onBinarySink;
if (activeMessageSink != null)
activeMessageSink.accept(frame.getPayload(),frame.isFin());
return;
}
case OpCode.TEXT:
{
if (activeMessageSink == null)
activeMessageSink = onTextSink;
if (activeMessageSink != null)
activeMessageSink.accept(frame.getPayload(),frame.isFin());
return;
}
case OpCode.CONTINUATION:
{
if (activeMessageSink != null)
activeMessageSink.accept(frame.getPayload(),frame.isFin());
return;
}
default:
{
if (LOG.isDebugEnabled())
LOG.debug("Unhandled OpCode: {}",opcode);
}
}
}
}
catch (NotUtf8Exception e)
{
notifyError(e);
close(StatusCode.BAD_PAYLOAD,e.getMessage());
}
catch (CloseException e)
{
close(e.getStatusCode(),e.getMessage());
}
catch (Throwable t)
{
LOG.warn("Unhandled Error (closing connection)",t);
notifyError(t);
// Unhandled Error, close the connection.
switch (policy.getBehavior())
{
case SERVER:
close(StatusCode.SERVER_ERROR,t.getClass().getSimpleName());
break;
case CLIENT:
close(StatusCode.POLICY_VIOLATION,t.getClass().getSimpleName());
break;
}
}
finally
{
// Unset active MessageSink if this was a fin frame
if (frame.isFin() && activeMessageSink != null)
activeMessageSink = null;
Thread.currentThread().setContextClassLoader(old);
}
}
@ -411,7 +757,8 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
{
LOG.debug("notifyClose({},{})",statusCode,reason);
}
websocket.onClose(new CloseInfo(statusCode,reason));
if (onCloseFunction != null)
onCloseFunction.apply(new CloseInfo(statusCode,reason));
}
public void notifyError(Throwable cause)
@ -470,11 +817,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
}
}
public WebSocketRemoteEndpoint newRemoteEndpoint(LogicalConnection connection, OutgoingFrames outgoingFrames, BatchMode batchMode)
{
return new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
}
/**
* Open/Activate the session
*/
@ -495,12 +837,13 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
connection.getIOState().onConnected();
// Connect remote
remote = remoteEndpointFactory.newRemoteEndpoint(connection,outgoingHandler,getBatchMode());
remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
if (LOG_OPEN.isDebugEnabled())
LOG_OPEN.debug("[{}] {}.open() remote={}",policy.getBehavior(),this.getClass().getSimpleName(),remote);
// Open WebSocket
websocket.openSession(this);
if (onOpenFunction != null)
onOpenFunction.apply(this);
// Open connection
connection.getIOState().onOpened();
@ -591,7 +934,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
*/
public BatchMode getBatchMode()
{
return BatchMode.AUTO;
return this.batchmode;
}
@Override
@ -599,11 +942,10 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
{
StringBuilder builder = new StringBuilder();
builder.append("WebSocketSession[");
builder.append("websocket=").append(websocket);
builder.append("websocket=").append(endpoint.getClass().getName());
builder.append(",behavior=").append(policy.getBehavior());
builder.append(",connection=").append(connection);
builder.append(",remote=").append(remote);
builder.append(",incoming=").append(incomingHandler);
builder.append(",outgoing=").append(outgoingHandler);
builder.append("]");
return builder.toString();

View File

@ -20,9 +20,8 @@ package org.eclipse.jetty.websocket.common;
import java.net.URI;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver;
import org.eclipse.jetty.websocket.common.events.JettyListenerEventDriver;
import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
/**
@ -38,13 +37,13 @@ public class WebSocketSessionFactory implements SessionFactory
}
@Override
public boolean supports(EventDriver websocket)
public boolean supports(Object websocket)
{
return (websocket instanceof JettyAnnotatedEventDriver) || (websocket instanceof JettyListenerEventDriver);
return (websocket instanceof WebSocketConnectionListener) || (websocket.getClass().getAnnotation(WebSocket.class) != null);
}
@Override
public WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection)
public WebSocketSession createSession(URI requestURI, Object websocket, LogicalConnection connection)
{
return new WebSocketSession(containerScope, requestURI,websocket,connection);
}

View File

@ -1,267 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.frames.CloseFrame;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
/**
* EventDriver is the main interface between the User's WebSocket POJO and the internal jetty implementation of WebSocket.
*/
public abstract class AbstractEventDriver extends AbstractLifeCycle implements IncomingFrames, EventDriver
{
private static final Logger LOG = Log.getLogger(AbstractEventDriver.class);
protected final Logger TARGET_LOG;
protected WebSocketPolicy policy;
protected final Object websocket;
protected WebSocketSession session;
protected MessageAppender activeMessage;
public AbstractEventDriver(WebSocketPolicy policy, Object websocket)
{
this.policy = policy;
this.websocket = websocket;
this.TARGET_LOG = Log.getLogger(websocket.getClass());
}
protected void appendMessage(ByteBuffer buffer, boolean fin) throws IOException
{
activeMessage.appendFrame(buffer,fin);
if (fin)
{
activeMessage.messageComplete();
activeMessage = null;
}
}
protected void dispatch(Runnable runnable)
{
session.dispatch(runnable);
}
@Override
public WebSocketPolicy getPolicy()
{
return policy;
}
@Override
public WebSocketSession getSession()
{
return session;
}
@Override
public final void incomingError(Throwable e)
{
if (LOG.isDebugEnabled())
{
LOG.debug("incomingError(" + e.getClass().getName() + ")",e);
}
onError(e);
}
@Override
public void incomingFrame(Frame frame)
{
if (LOG.isDebugEnabled())
{
LOG.debug("incomingFrame({})",frame);
}
try
{
onFrame(frame);
byte opcode = frame.getOpCode();
switch (opcode)
{
case OpCode.CLOSE:
{
boolean validate = true;
CloseFrame closeframe = (CloseFrame)frame;
CloseInfo close = new CloseInfo(closeframe,validate);
// process handshake
session.getConnection().getIOState().onCloseRemote(close);
return;
}
case OpCode.PING:
{
if (LOG.isDebugEnabled())
{
LOG.debug("PING: {}",BufferUtil.toDetailString(frame.getPayload()));
}
ByteBuffer pongBuf;
if (frame.hasPayload())
{
pongBuf = ByteBuffer.allocate(frame.getPayload().remaining());
BufferUtil.put(frame.getPayload().slice(),pongBuf);
BufferUtil.flipToFlush(pongBuf,0);
}
else
{
pongBuf = ByteBuffer.allocate(0);
}
onPing(frame.getPayload());
session.getRemote().sendPong(pongBuf);
break;
}
case OpCode.PONG:
{
if (LOG.isDebugEnabled())
{
LOG.debug("PONG: {}",BufferUtil.toDetailString(frame.getPayload()));
}
onPong(frame.getPayload());
break;
}
case OpCode.BINARY:
{
onBinaryFrame(frame.getPayload(),frame.isFin());
return;
}
case OpCode.TEXT:
{
onTextFrame(frame.getPayload(),frame.isFin());
return;
}
case OpCode.CONTINUATION:
{
onContinuationFrame(frame.getPayload(),frame.isFin());
return;
}
default:
{
if (LOG.isDebugEnabled())
LOG.debug("Unhandled OpCode: {}",opcode);
}
}
}
catch (NotUtf8Exception e)
{
terminateConnection(StatusCode.BAD_PAYLOAD,e.getMessage());
}
catch (CloseException e)
{
terminateConnection(e.getStatusCode(),e.getMessage());
}
catch (Throwable t)
{
unhandled(t);
}
}
@Override
public void onContinuationFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (activeMessage == null)
{
throw new IOException("Out of order Continuation frame encountered");
}
appendMessage(buffer,fin);
}
@Override
public void onPong(ByteBuffer buffer)
{
/* TODO: provide annotation in future */
}
@Override
public void onPing(ByteBuffer buffer)
{
/* TODO: provide annotation in future */
}
@Override
public BatchMode getBatchMode()
{
return null;
}
@Override
public void openSession(WebSocketSession session)
{
if (LOG.isDebugEnabled())
LOG.debug("openSession({})",session);
this.session = session;
this.session.getContainerScope().getObjectFactory().decorate(this.websocket);
try
{
this.onConnect();
}
catch (Throwable t)
{
unhandled(t);
throw t;
}
}
protected void terminateConnection(int statusCode, String rawreason)
{
if (LOG.isDebugEnabled())
LOG.debug("terminateConnection({},{})",statusCode,rawreason);
session.close(statusCode,CloseFrame.truncate(rawreason));
}
private void unhandled(Throwable t)
{
TARGET_LOG.warn("Unhandled Error (closing connection)",t);
onError(t);
if (t instanceof CloseException)
{
terminateConnection(((CloseException)t).getStatusCode(),t.getClass().getSimpleName());
return;
}
// Unhandled Error, close the connection.
switch (policy.getBehavior())
{
case SERVER:
terminateConnection(StatusCode.SERVER_ERROR,t.getClass().getSimpleName());
break;
case CLIENT:
terminateConnection(StatusCode.POLICY_VIOLATION,t.getClass().getSimpleName());
break;
}
}
}

View File

@ -1,68 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.WebSocketSession;
public interface EventDriver extends IncomingFrames
{
public WebSocketPolicy getPolicy();
public WebSocketSession getSession();
public BatchMode getBatchMode();
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException;
public void onBinaryMessage(byte[] data);
public void onClose(CloseInfo close);
public void onConnect();
public void onContinuationFrame(ByteBuffer buffer, boolean fin) throws IOException;
public void onError(Throwable t);
public void onFrame(Frame frame);
public void onInputStream(InputStream stream) throws IOException;
public void onPing(ByteBuffer buffer);
public void onPong(ByteBuffer buffer);
public void onReader(Reader reader) throws IOException;
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException;
public void onTextMessage(String message);
public void openSession(WebSocketSession session);
}

View File

@ -1,149 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
/**
* Create EventDriver implementations.
*/
public class EventDriverFactory
{
private static final Logger LOG = Log.getLogger(EventDriverFactory.class);
private final WebSocketPolicy policy;
private final List<EventDriverImpl> implementations;
public EventDriverFactory(WebSocketPolicy policy)
{
this.policy = policy;
this.implementations = new ArrayList<>();
addImplementation(new JettyListenerImpl());
addImplementation(new JettyAnnotatedImpl());
}
public void addImplementation(EventDriverImpl impl)
{
if (implementations.contains(impl))
{
LOG.warn("Ignoring attempt to add duplicate EventDriverImpl: " + impl);
return;
}
implementations.add(impl);
}
public void clearImplementations()
{
this.implementations.clear();
}
protected String getClassName(Object websocket)
{
return websocket.getClass().getName();
}
public List<EventDriverImpl> getImplementations()
{
return implementations;
}
public boolean removeImplementation(EventDriverImpl impl)
{
return this.implementations.remove(impl);
}
@Override
public String toString()
{
StringBuilder msg = new StringBuilder();
msg.append(this.getClass().getSimpleName());
msg.append("[implementations=[");
boolean delim = false;
for (EventDriverImpl impl : implementations)
{
if (delim)
{
msg.append(',');
}
msg.append(impl.toString());
delim = true;
}
msg.append("]");
return msg.toString();
}
/**
* Wrap the given WebSocket object instance in a suitable EventDriver
*
* @param websocket
* the websocket instance to wrap. Must either implement {@link WebSocketListener} or be annotated with {@link WebSocket &#064;WebSocket}
* @return appropriate EventDriver for this websocket instance.
*/
public EventDriver wrap(Object websocket)
{
if (websocket == null)
{
throw new InvalidWebSocketException("null websocket object");
}
for (EventDriverImpl impl : implementations)
{
if (impl.supports(websocket))
{
try
{
return impl.create(websocket,policy.clonePolicy());
}
catch (Throwable e)
{
throw new InvalidWebSocketException("Unable to create websocket",e);
}
}
}
// Create a clear error message for the developer
StringBuilder err = new StringBuilder();
err.append(getClassName(websocket));
err.append(" is not a valid WebSocket object.");
err.append(" Object must obey one of the following rules: ");
int len = implementations.size();
for (int i = 0; i < len; i++)
{
EventDriverImpl impl = implementations.get(i);
if (i > 0)
{
err.append(" or ");
}
err.append("\n(").append(i + 1).append(") ");
err.append(impl.describeRule());
}
throw new InvalidWebSocketException(err.toString());
}
}

View File

@ -1,58 +0,0 @@
//
// ========================================================================
// 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.common.events;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
/**
* A specific implementation of a EventDriver.
*/
public interface EventDriverImpl
{
/**
* Create the EventDriver based on this implementation.
*
* @param websocket
* the websocket to wrap
* @param policy
* the policy to use
* @return the created EventDriver
* @throws Throwable
* if unable to create the EventDriver
*/
EventDriver create(Object websocket, WebSocketPolicy policy) throws Throwable;
/**
* human readable string describing the rule that would support this EventDriver.
* <p>
* Used to help developer with possible object annotations, listeners, or base classes.
*
* @return the human readable description of this event driver rule(s).
*/
String describeRule();
/**
* Test for if this implementation can support the provided websocket.
*
* @param websocket
* the possible websocket to test
* @return true if implementation can support it, false if otherwise.
*/
boolean supports(Object websocket);
}

View File

@ -1,243 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.common.message.MessageInputStream;
import org.eclipse.jetty.websocket.common.message.MessageReader;
import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
/**
* Handler for Annotated User WebSocket objects.
*/
public class JettyAnnotatedEventDriver extends AbstractEventDriver
{
private final JettyAnnotatedMetadata events;
private boolean hasCloseBeenCalled = false;
private BatchMode batchMode;
public JettyAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JettyAnnotatedMetadata events)
{
super(policy,websocket);
this.events = events;
WebSocket anno = websocket.getClass().getAnnotation(WebSocket.class);
// Setup the policy
if (anno.maxTextMessageSize() > 0)
{
this.policy.setMaxTextMessageSize(anno.maxTextMessageSize());
}
if (anno.maxBinaryMessageSize() > 0)
{
this.policy.setMaxBinaryMessageSize(anno.maxBinaryMessageSize());
}
if (anno.inputBufferSize() > 0)
{
this.policy.setInputBufferSize(anno.inputBufferSize());
}
if (anno.maxIdleTime() > 0)
{
this.policy.setIdleTimeout(anno.maxIdleTime());
}
this.batchMode = anno.batchMode();
}
@Override
public BatchMode getBatchMode()
{
return this.batchMode;
}
@Override
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (events.onBinary == null)
{
// not interested in binary events
return;
}
if (activeMessage == null)
{
if (events.onBinary.isStreaming())
{
activeMessage = new MessageInputStream();
final MessageAppender msg = activeMessage;
dispatch(new Runnable()
{
@Override
public void run()
{
try
{
events.onBinary.call(websocket,session,msg);
}
catch (Throwable t)
{
// dispatched calls need to be reported
onError(t);
}
}
});
}
else
{
activeMessage = new SimpleBinaryMessage(this);
}
}
appendMessage(buffer,fin);
}
@Override
public void onBinaryMessage(byte[] data)
{
if (events.onBinary != null)
{
events.onBinary.call(websocket,session,data,0,data.length);
}
}
@Override
public void onClose(CloseInfo close)
{
if (hasCloseBeenCalled)
{
// avoid duplicate close events (possible when using harsh Session.disconnect())
return;
}
hasCloseBeenCalled = true;
if (events.onClose != null)
{
events.onClose.call(websocket,session,close.getStatusCode(),close.getReason());
}
}
@Override
public void onConnect()
{
if (events.onConnect != null)
{
events.onConnect.call(websocket,session);
}
}
@Override
public void onError(Throwable cause)
{
if (events.onError != null)
{
events.onError.call(websocket,session,cause);
}
}
@Override
public void onFrame(Frame frame)
{
if (events.onFrame != null)
{
events.onFrame.call(websocket,session,frame);
}
}
@Override
public void onInputStream(InputStream stream)
{
if (events.onBinary != null)
{
events.onBinary.call(websocket,session,stream);
}
}
@Override
public void onReader(Reader reader)
{
if (events.onText != null)
{
events.onText.call(websocket,session,reader);
}
}
@Override
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (events.onText == null)
{
// not interested in text events
return;
}
if (activeMessage == null)
{
if (events.onText.isStreaming())
{
activeMessage = new MessageReader(new MessageInputStream());
final MessageAppender msg = activeMessage;
dispatch(new Runnable()
{
@Override
public void run()
{
try
{
events.onText.call(websocket,session,msg);
}
catch (Throwable t)
{
// dispatched calls need to be reported
onError(t);
}
}
});
}
else
{
activeMessage = new SimpleTextMessage(this);
}
}
appendMessage(buffer,fin);
}
@Override
public void onTextMessage(String message)
{
if (events.onText != null)
{
events.onText.call(websocket,session,message);
}
}
@Override
public String toString()
{
return String.format("%s[%s]", this.getClass().getSimpleName(), websocket);
}
}

View File

@ -1,65 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
public class JettyAnnotatedImpl implements EventDriverImpl
{
private ConcurrentHashMap<Class<?>, JettyAnnotatedMetadata> cache = new ConcurrentHashMap<>();
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
Class<?> websocketClass = websocket.getClass();
synchronized (this)
{
JettyAnnotatedMetadata metadata = cache.get(websocketClass);
if (metadata == null)
{
JettyAnnotatedScanner scanner = new JettyAnnotatedScanner();
metadata = scanner.scan(websocketClass);
cache.put(websocketClass,metadata);
}
return new JettyAnnotatedEventDriver(policy,websocket,metadata);
}
}
@Override
public String describeRule()
{
return "class is annotated with @" + WebSocket.class.getName();
}
@Override
public boolean supports(Object websocket)
{
WebSocket anno = websocket.getClass().getAnnotation(WebSocket.class);
return (anno != null);
}
@Override
public String toString()
{
return String.format("%s [cache.count=%d]",this.getClass().getSimpleName(),cache.size());
}
}

View File

@ -1,53 +0,0 @@
//
// ========================================================================
// 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.common.events;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod;
public class JettyAnnotatedMetadata
{
/** &#064;OnWebSocketConnect () */
public CallableMethod onConnect;
/** &#064;OnWebSocketMessage (byte[], or ByteBuffer, or InputStream) */
public OptionalSessionCallableMethod onBinary;
/** &#064;OnWebSocketMessage (String, or Reader) */
public OptionalSessionCallableMethod onText;
/** &#064;OnWebSocketFrame (Frame) */
public OptionalSessionCallableMethod onFrame;
/** &#064;OnWebSocketError (Throwable) */
public OptionalSessionCallableMethod onError;
/** &#064;OnWebSocketClose (Frame) */
public OptionalSessionCallableMethod onClose;
@Override
public String toString()
{
StringBuilder s = new StringBuilder();
s.append("JettyPojoMetadata[");
s.append("onConnect=").append(onConnect);
s.append(",onBinary=").append(onBinary);
s.append(",onText=").append(onText);
s.append(",onFrame=").append(onFrame);
s.append(",onError=").append(onError);
s.append(",onClose=").append(onClose);
s.append("]");
return s.toString();
}
}

View File

@ -1,171 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.io.InputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotationScanner;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod;
public class JettyAnnotatedScanner extends AbstractMethodAnnotationScanner<JettyAnnotatedMetadata>
{
private static final Logger LOG = Log.getLogger(JettyAnnotatedScanner.class);
/**
* Parameter list for &#064;OnWebSocketMessage (Binary mode)
*/
private static final ParamList validBinaryParams;
/**
* Parameter list for &#064;OnWebSocketConnect
*/
private static final ParamList validConnectParams;
/**
* Parameter list for &#064;OnWebSocketClose
*/
private static final ParamList validCloseParams;
/**
* Parameter list for &#064;OnWebSocketError
*/
private static final ParamList validErrorParams;
/**
* Parameter list for &#064;OnWebSocketFrame
*/
private static final ParamList validFrameParams;
/**
* Parameter list for &#064;OnWebSocketMessage (Text mode)
*/
private static final ParamList validTextParams;
static
{
validConnectParams = new ParamList();
validConnectParams.addParams(Session.class);
validCloseParams = new ParamList();
validCloseParams.addParams(int.class,String.class);
validCloseParams.addParams(Session.class,int.class,String.class);
validErrorParams = new ParamList();
validErrorParams.addParams(Throwable.class);
validErrorParams.addParams(Session.class,Throwable.class);
validTextParams = new ParamList();
validTextParams.addParams(String.class);
validTextParams.addParams(Session.class,String.class);
validTextParams.addParams(Reader.class);
validTextParams.addParams(Session.class,Reader.class);
validBinaryParams = new ParamList();
validBinaryParams.addParams(byte[].class,int.class,int.class);
validBinaryParams.addParams(Session.class,byte[].class,int.class,int.class);
validBinaryParams.addParams(InputStream.class);
validBinaryParams.addParams(Session.class,InputStream.class);
validFrameParams = new ParamList();
validFrameParams.addParams(Frame.class);
validFrameParams.addParams(Session.class,Frame.class);
}
@Override
public void onMethodAnnotation(JettyAnnotatedMetadata metadata, Class<?> pojo, Method method, Annotation annotation)
{
if (LOG.isDebugEnabled())
LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
if (isAnnotation(annotation,OnWebSocketConnect.class))
{
assertValidSignature(method,OnWebSocketConnect.class,validConnectParams);
assertUnset(metadata.onConnect,OnWebSocketConnect.class,method);
metadata.onConnect = new CallableMethod(pojo,method);
return;
}
if (isAnnotation(annotation,OnWebSocketMessage.class))
{
if (isSignatureMatch(method,validTextParams))
{
// Text mode
assertUnset(metadata.onText,OnWebSocketMessage.class,method);
metadata.onText = new OptionalSessionCallableMethod(pojo,method);
return;
}
if (isSignatureMatch(method,validBinaryParams))
{
// Binary Mode
// TODO
assertUnset(metadata.onBinary,OnWebSocketMessage.class,method);
metadata.onBinary = new OptionalSessionCallableMethod(pojo,method);
return;
}
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,validTextParams,validBinaryParams);
}
if (isAnnotation(annotation,OnWebSocketClose.class))
{
assertValidSignature(method,OnWebSocketClose.class,validCloseParams);
assertUnset(metadata.onClose,OnWebSocketClose.class,method);
metadata.onClose = new OptionalSessionCallableMethod(pojo,method);
return;
}
if (isAnnotation(annotation,OnWebSocketError.class))
{
assertValidSignature(method,OnWebSocketError.class,validErrorParams);
assertUnset(metadata.onError,OnWebSocketError.class,method);
metadata.onError = new OptionalSessionCallableMethod(pojo,method);
return;
}
if (isAnnotation(annotation,OnWebSocketFrame.class))
{
assertValidSignature(method,OnWebSocketFrame.class,validFrameParams);
assertUnset(metadata.onFrame,OnWebSocketFrame.class,method);
metadata.onFrame = new OptionalSessionCallableMethod(pojo,method);
return;
}
}
public JettyAnnotatedMetadata scan(Class<?> pojo)
{
JettyAnnotatedMetadata metadata = new JettyAnnotatedMetadata();
scanMethodAnnotations(metadata,pojo);
return metadata;
}
}

View File

@ -1,198 +0,0 @@
//
// ========================================================================
// 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.common.events;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
import org.eclipse.jetty.websocket.api.WebSocketFrameListener;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
import org.eclipse.jetty.websocket.api.WebSocketPingPongListener;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.Frame.Type;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.frames.ReadOnlyDelegatedFrame;
import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
import org.eclipse.jetty.websocket.common.util.Utf8PartialBuilder;
/**
* Handler for {@link WebSocketListener} based User WebSocket implementations.
*/
public class JettyListenerEventDriver extends AbstractEventDriver
{
private static final Logger LOG = Log.getLogger(JettyListenerEventDriver.class);
private final WebSocketConnectionListener listener;
private Utf8PartialBuilder utf8Partial;
private boolean hasCloseBeenCalled = false;
public JettyListenerEventDriver(WebSocketPolicy policy, WebSocketConnectionListener listener)
{
super(policy,listener);
this.listener = listener;
}
@Override
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (listener instanceof WebSocketListener)
{
if (activeMessage == null)
{
activeMessage = new SimpleBinaryMessage(this);
}
appendMessage(buffer,fin);
}
if (listener instanceof WebSocketPartialListener)
{
((WebSocketPartialListener)listener).onWebSocketPartialBinary(buffer.slice().asReadOnlyBuffer(),fin);
}
}
@Override
public void onBinaryMessage(byte[] data)
{
if (listener instanceof WebSocketListener)
{
((WebSocketListener)listener).onWebSocketBinary(data,0,data.length);
}
}
@Override
public void onClose(CloseInfo close)
{
if (hasCloseBeenCalled)
{
// avoid duplicate close events (possible when using harsh Session.disconnect())
return;
}
hasCloseBeenCalled = true;
int statusCode = close.getStatusCode();
String reason = close.getReason();
listener.onWebSocketClose(statusCode,reason);
}
@Override
public void onConnect()
{
if (LOG.isDebugEnabled())
LOG.debug("onConnect()");
listener.onWebSocketConnect(session);
}
@Override
public void onError(Throwable cause)
{
listener.onWebSocketError(cause);
}
@Override
public void onFrame(Frame frame)
{
if (listener instanceof WebSocketFrameListener)
{
((WebSocketFrameListener)listener).onWebSocketFrame(new ReadOnlyDelegatedFrame(frame));
}
if (listener instanceof WebSocketPingPongListener)
{
if (frame.getType() == Type.PING)
{
((WebSocketPingPongListener)listener).onWebSocketPing(frame.getPayload().asReadOnlyBuffer());
}
else if (frame.getType() == Type.PONG)
{
((WebSocketPingPongListener)listener).onWebSocketPong(frame.getPayload().asReadOnlyBuffer());
}
}
}
@Override
public void onInputStream(InputStream stream)
{
/* not supported in Listener mode (yet) */
}
@Override
public void onReader(Reader reader)
{
/* not supported in Listener mode (yet) */
}
@Override
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (listener instanceof WebSocketListener)
{
if (activeMessage == null)
{
activeMessage = new SimpleTextMessage(this);
}
appendMessage(buffer,fin);
}
if (listener instanceof WebSocketPartialListener)
{
if (utf8Partial == null)
{
utf8Partial = new Utf8PartialBuilder();
}
String partial = utf8Partial.toPartialString(buffer);
((WebSocketPartialListener)listener).onWebSocketPartialText(partial,fin);
if (fin)
{
partial = null;
}
}
}
/**
* Whole Message event.
*
* @param message the whole message
*/
@Override
public void onTextMessage(String message)
{
if (listener instanceof WebSocketListener)
{
((WebSocketListener)listener).onWebSocketText(message);
}
}
@Override
public String toString()
{
return String.format("%s[%s]",JettyListenerEventDriver.class.getSimpleName(),listener.getClass().getName());
}
}

View File

@ -1,195 +0,0 @@
//
// ========================================================================
// 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.common.events.annotated;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.events.ParamList;
/**
* Basic scanner for Annotated Methods
* @param <T> The type of metadata
*/
public abstract class AbstractMethodAnnotationScanner<T>
{
protected void assertIsPublicNonStatic(Method method)
{
int mods = method.getModifiers();
if (!Modifier.isPublic(mods))
{
StringBuilder err = new StringBuilder();
err.append("Invalid declaration of ");
err.append(method);
err.append(System.lineSeparator());
err.append("Method modifier must be public");
throw new InvalidWebSocketException(err.toString());
}
if (Modifier.isStatic(mods))
{
StringBuilder err = new StringBuilder();
err.append("Invalid declaration of ");
err.append(method);
err.append(System.lineSeparator());
err.append("Method modifier may not be static");
throw new InvalidWebSocketException(err.toString());
}
}
protected void assertIsReturn(Method method, Class<?> type)
{
if (!type.equals(method.getReturnType()))
{
StringBuilder err = new StringBuilder();
err.append("Invalid declaration of ");
err.append(method);
err.append(System.lineSeparator());
err.append("Return type must be ").append(type);
throw new InvalidWebSocketException(err.toString());
}
}
protected void assertIsVoidReturn(Method method)
{
assertIsReturn(method,Void.TYPE);
}
protected void assertUnset(CallableMethod callable, Class<? extends Annotation> annoClass, Method method)
{
if (callable != null)
{
// Attempt to add duplicate frame type (a no-no)
StringBuilder err = new StringBuilder();
err.append("Duplicate @").append(annoClass.getSimpleName()).append(" declaration on ");
err.append(method);
err.append(System.lineSeparator());
err.append("@").append(annoClass.getSimpleName()).append(" previously declared at ");
err.append(callable.getMethod());
throw new InvalidWebSocketException(err.toString());
}
}
protected void assertValidSignature(Method method, Class<? extends Annotation> annoClass, ParamList validParams)
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
boolean valid = false;
// validate parameters
Class<?> actual[] = method.getParameterTypes();
for (Class<?>[] params : validParams)
{
if (isSameParameters(actual,params))
{
valid = true;
break;
}
}
if (!valid)
{
throw InvalidSignatureException.build(method,annoClass,validParams);
}
}
public boolean isAnnotation(Annotation annotation, Class<? extends Annotation> annotationClass)
{
return annotation.annotationType().equals(annotationClass);
}
public boolean isSameParameters(Class<?>[] actual, Class<?>[] params)
{
if (actual.length != params.length)
{
// skip
return false;
}
int len = params.length;
for (int i = 0; i < len; i++)
{
if (!actual[i].equals(params[i]))
{
return false; // not valid
}
}
return true;
}
protected boolean isSignatureMatch(Method method, ParamList validParams)
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
// validate parameters
Class<?> actual[] = method.getParameterTypes();
for (Class<?>[] params : validParams)
{
if (isSameParameters(actual,params))
{
return true;
}
}
return false;
}
protected boolean isTypeAnnotated(Class<?> pojo, Class<? extends Annotation> expectedAnnotation)
{
return pojo.getAnnotation(expectedAnnotation) != null;
}
public abstract void onMethodAnnotation(T metadata, Class<?> pojo, Method method, Annotation annotation);
public void scanMethodAnnotations(T metadata, Class<?> pojo)
{
Class<?> clazz = pojo;
while ((clazz != null) && Object.class.isAssignableFrom(clazz))
{
for (Method method : clazz.getDeclaredMethods())
{
Annotation annotations[] = method.getAnnotations();
if ((annotations == null) || (annotations.length <= 0))
{
continue; // skip
}
for (Annotation annotation : annotations)
{
onMethodAnnotation(metadata,clazz,method,annotation);
}
}
clazz = clazz.getSuperclass();
}
}
}

View File

@ -1,145 +0,0 @@
//
// ========================================================================
// 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.common.events.annotated;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
/**
* A Callable Method
*/
public class CallableMethod
{
private static final Logger LOG = Log.getLogger(CallableMethod.class);
protected final Class<?> pojo;
protected final Method method;
protected Class<?>[] paramTypes;
public CallableMethod(Class<?> pojo, Method method)
{
Objects.requireNonNull(pojo, "Pojo cannot be null");
Objects.requireNonNull(method, "Method cannot be null");
this.pojo = pojo;
this.method = method;
this.paramTypes = method.getParameterTypes();
}
public Object call(Object obj, Object... args)
{
if ((this.pojo == null) || (this.method == null))
{
LOG.warn("Cannot execute call: pojo={}, method={}",pojo,method);
return null; // no call event method determined
}
if (obj == null)
{
String err = String.format("Cannot call %s on null object", this.method);
LOG.warn(new RuntimeException(err));
return null;
}
if (args.length < paramTypes.length)
{
throw new IllegalArgumentException("Call arguments length [" + args.length + "] must always be greater than or equal to captured args length ["
+ paramTypes.length + "]");
}
try
{
return this.method.invoke(obj,args);
}
catch (Throwable t)
{
String err = formatMethodCallError(args);
throw unwrapRuntimeException(err,t);
}
}
private RuntimeException unwrapRuntimeException(String err, final Throwable t)
{
Throwable ret = t;
while (ret instanceof InvocationTargetException)
{
ret = ((InvocationTargetException)ret).getCause();
}
if (ret instanceof RuntimeException)
{
return (RuntimeException)ret;
}
return new RuntimeException(err,ret);
}
public String formatMethodCallError(Object... args)
{
StringBuilder err = new StringBuilder();
err.append("Cannot call method ");
err.append(ReflectUtils.toString(pojo,method));
err.append(" with args: [");
boolean delim = false;
for (Object arg : args)
{
if (delim)
{
err.append(", ");
}
if (arg == null)
{
err.append("<null>");
}
else
{
err.append(arg.getClass().getName());
}
delim = true;
}
err.append("]");
return err.toString();
}
public Method getMethod()
{
return method;
}
public Class<?>[] getParamTypes()
{
return paramTypes;
}
public Class<?> getPojo()
{
return pojo;
}
@Override
public String toString()
{
return String.format("%s[%s]",this.getClass().getSimpleName(),method.toGenericString());
}
}

View File

@ -1,154 +0,0 @@
//
// ========================================================================
// 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.common.events.annotated;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.util.QuoteUtil;
public class EventMethod
{
private static final Logger LOG = Log.getLogger(EventMethod.class);
private static Object[] dropFirstArg(Object[] args)
{
if (args.length == 1)
{
return new Object[0];
}
Object ret[] = new Object[args.length - 1];
System.arraycopy(args,1,ret,0,ret.length);
return ret;
}
protected Class<?> pojo;
protected Method method;
private boolean hasSession = false;
private boolean isStreaming = false;
private Class<?>[] paramTypes;
public EventMethod(Class<?> pojo, Method method)
{
this.pojo = pojo;
this.paramTypes = method.getParameterTypes();
this.method = method;
identifyPresentParamTypes();
}
public EventMethod(Class<?> pojo, String methodName, Class<?>... paramTypes)
{
try
{
this.pojo = pojo;
this.paramTypes = paramTypes;
this.method = pojo.getMethod(methodName,paramTypes);
identifyPresentParamTypes();
}
catch (NoSuchMethodException | SecurityException e)
{
LOG.warn("Cannot use method {}({}): {}",methodName,paramTypes,e.getMessage());
this.method = null;
}
}
public void call(Object obj, Object... args)
{
if ((this.pojo == null) || (this.method == null))
{
LOG.warn("Cannot execute call: pojo={}, method={}",pojo,method);
return; // no call event method determined
}
if (obj == null)
{
LOG.warn("Cannot call {} on null object",this.method);
return;
}
if (args.length > paramTypes.length)
{
Object trimArgs[] = dropFirstArg(args);
call(obj,trimArgs);
return;
}
if (args.length < paramTypes.length)
{
throw new IllegalArgumentException("Call arguments length [" + args.length + "] must always be greater than or equal to captured args length ["
+ paramTypes.length + "]");
}
try
{
this.method.invoke(obj,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
String err = String.format("Cannot call method %s on %s with args: %s",method,pojo, QuoteUtil.join(args,","));
throw new WebSocketException(err,e);
}
}
public Method getMethod()
{
return method;
}
protected Class<?>[] getParamTypes()
{
return this.paramTypes;
}
private void identifyPresentParamTypes()
{
this.hasSession = false;
this.isStreaming = false;
if (paramTypes == null)
{
return;
}
for (Class<?> paramType : paramTypes)
{
if (Session.class.isAssignableFrom(paramType))
{
this.hasSession = true;
}
if (Reader.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType))
{
this.isStreaming = true;
}
}
}
public boolean isHasSession()
{
return hasSession;
}
public boolean isStreaming()
{
return isStreaming;
}
}

View File

@ -1,105 +0,0 @@
//
// ========================================================================
// 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.common.events.annotated;
/**
* A representation of the methods available to call for a particular class.
*/
public class EventMethods
{
private Class<?> pojoClass;
public EventMethod onConnect = null;
public EventMethod onClose = null;
public EventMethod onBinary = null;
public EventMethod onText = null;
public EventMethod onError = null;
public EventMethod onFrame = null;
public EventMethods(Class<?> pojoClass)
{
this.pojoClass = pojoClass;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
EventMethods other = (EventMethods)obj;
if (pojoClass == null)
{
if (other.pojoClass != null)
{
return false;
}
}
else if (!pojoClass.getName().equals(other.pojoClass.getName()))
{
return false;
}
return true;
}
public Class<?> getPojoClass()
{
return pojoClass;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + ((pojoClass == null)?0:pojoClass.getName().hashCode());
return result;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("EventMethods [pojoClass=");
builder.append(pojoClass);
builder.append(", onConnect=");
builder.append(onConnect);
builder.append(", onClose=");
builder.append(onClose);
builder.append(", onBinary=");
builder.append(onBinary);
builder.append(", onText=");
builder.append(onText);
builder.append(", onException=");
builder.append(onError);
builder.append(", onFrame=");
builder.append(onFrame);
builder.append("]");
return builder.toString();
}
}

View File

@ -1,91 +0,0 @@
//
// ========================================================================
// 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.common.events.annotated;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Method;
import org.eclipse.jetty.websocket.api.Session;
/**
* Simple CallableMethod that manages the optional {@link Session} argument
*/
public class OptionalSessionCallableMethod extends CallableMethod
{
private final boolean wantsSession;
private final boolean streaming;
public OptionalSessionCallableMethod(Class<?> pojo, Method method)
{
super(pojo,method);
boolean foundConnection = false;
boolean foundStreaming = false;
if (paramTypes != null)
{
for (Class<?> paramType : paramTypes)
{
if (Session.class.isAssignableFrom(paramType))
{
foundConnection = true;
}
if (Reader.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType))
{
foundStreaming = true;
}
}
}
this.wantsSession = foundConnection;
this.streaming = foundStreaming;
}
public void call(Object obj, Session connection, Object... args)
{
if (wantsSession)
{
Object fullArgs[] = new Object[args.length + 1];
fullArgs[0] = connection;
System.arraycopy(args,0,fullArgs,1,args.length);
call(obj,fullArgs);
}
else
{
call(obj,args);
}
}
public boolean isSessionAware()
{
return wantsSession;
}
public boolean isStreaming()
{
return streaming;
}
@Override
public String toString()
{
return String.format("%s[%s]",this.getClass().getSimpleName(),method.toGenericString());
}
}

View File

@ -0,0 +1,101 @@
//
// ========================================================================
// 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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
/**
* Jetty {@link WebSocket} {@link OnWebSocketMessage} method {@link Function} for BINARY/byte[] types
*/
public class OnByteArrayFunction implements Function<byte[], Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int BUFFER = 2;
private static final int OFFSET = 3;
private static final int LENGTH = 4;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(byte[].class).indexedAs(BUFFER));
ARGBUILDER.addSignature(new ExactSignature(byte[].class,int.class,int.class).indexedAs(BUFFER,OFFSET,LENGTH));
ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class).indexedAs(SESSION,BUFFER));
ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class,int.class,int.class).indexedAs(SESSION,BUFFER,OFFSET,LENGTH));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnByteArrayFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,BUFFER,OFFSET,LENGTH);
}
@Override
public Void apply(byte[] bin)
{
Object args[] = this.callable.toArgs(session,bin,0,bin.length);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,98 @@
//
// ========================================================================
// 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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketMessage} method {@link Function} for BINARY/{@link ByteBuffer} types
*/
public class OnByteBufferFunction implements Function<ByteBuffer, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int BUFFER = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(ByteBuffer.class).indexedAs(BUFFER));
ARGBUILDER.addSignature(new ExactSignature(Session.class,ByteBuffer.class).indexedAs(SESSION,BUFFER));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnByteBufferFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,BUFFER);
}
@Override
public Void apply(ByteBuffer bin)
{
Object args[] = this.callable.toArgs(session,bin);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// 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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketClose} method {@link Function}
*/
public class OnCloseFunction implements Function<CloseInfo, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int STATUS_CODE = 2;
private static final int REASON = 3;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature().indexedAs());
ARGBUILDER.addSignature(new ExactSignature(Session.class).indexedAs(SESSION));
ARGBUILDER.addSignature(new ExactSignature(int.class,String.class).indexedAs(STATUS_CODE,REASON));
ARGBUILDER.addSignature(new ExactSignature(Session.class,int.class,String.class).indexedAs(SESSION,STATUS_CODE,REASON));
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnCloseFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketClose.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketClose.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,STATUS_CODE,REASON);
}
@Override
public Void apply(CloseInfo closeinfo)
{
Object args[] = this.callable.toArgs(session,closeinfo.getStatusCode(),closeinfo.getReason());
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call close method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,87 @@
//
// ========================================================================
// 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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketError} method {@link Function}
*/
public class OnErrorFunction implements Function<Throwable, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int CAUSE = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(Throwable.class).indexedAs(CAUSE));
ARGBUILDER.addSignature(new ExactSignature(Session.class,Throwable.class).indexedAs(SESSION,CAUSE));
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnErrorFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketError.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketError.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,CAUSE);
}
@Override
public Void apply(Throwable cause)
{
Object args[] = this.callable.toArgs(session,cause);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call error method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,90 @@
//
// ========================================================================
// 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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketFrame} method {@link Function}
*/
public class OnFrameFunction implements Function<Frame, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int FRAME = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(Frame.class).indexedAs(FRAME));
ARGBUILDER.addSignature(new ExactSignature(Session.class,Frame.class).indexedAs(SESSION,FRAME));
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnFrameFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketFrame.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketFrame.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,FRAME);
}
@Override
public Void apply(Frame frame)
{
WebSocketFrame copy = WebSocketFrame.copy(frame);
Object args[] = this.callable.toArgs(session,copy);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call frame method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,99 @@
//
// ========================================================================
// 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.common.functions;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketMessage} method {@link Function} for BINARY/{@link InputStream} streaming
* types
*/
public class OnInputStreamFunction implements Function<InputStream, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int STREAM = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(InputStream.class).indexedAs(STREAM));
ARGBUILDER.addSignature(new ExactSignature(Session.class,InputStream.class).indexedAs(SESSION,STREAM));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnInputStreamFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,STREAM);
}
@Override
public Void apply(InputStream stream)
{
Object args[] = this.callable.toArgs(session,stream);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketConnect} method {@link Function}
*/
public class OnOpenFunction implements Function<Session, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature().indexedAs());
ARGBUILDER.addSignature(new ExactSignature(Session.class).indexedAs(SESSION));
}
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnOpenFunction(Object endpoint, Method method)
{
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketConnect.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketConnect.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION);
}
@Override
public Void apply(Session session)
{
Object args[] = this.callable.toArgs(session);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,98 @@
//
// ========================================================================
// 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.common.functions;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketMessage} method {@link Function} for TEXT/{@link Reader} streaming types
*/
public class OnReaderFunction implements Function<Reader, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int STREAM = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(Reader.class).indexedAs(STREAM));
ARGBUILDER.addSignature(new ExactSignature(Session.class,Reader.class).indexedAs(SESSION,STREAM));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnReaderFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,STREAM);
}
@Override
public Void apply(Reader stream)
{
Object args[] = this.callable.toArgs(session,stream);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -0,0 +1,97 @@
//
// ========================================================================
// 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.common.functions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.ExactSignature;
/**
* Jetty {@link WebSocket} {@link OnWebSocketMessage} method {@link Function} for TEXT/{@link String} types
*/
public class OnTextFunction implements Function<String, Void>
{
private static final DynamicArgs.Builder ARGBUILDER;
private static final int SESSION = 1;
private static final int TEXT = 2;
static
{
ARGBUILDER = new DynamicArgs.Builder();
ARGBUILDER.addSignature(new ExactSignature(String.class).indexedAs(TEXT));
ARGBUILDER.addSignature(new ExactSignature(Session.class,String.class).indexedAs(SESSION,TEXT));
}
public static DynamicArgs.Builder getDynamicArgsBuilder()
{
return ARGBUILDER;
}
public static boolean hasMatchingSignature(Method method)
{
return ARGBUILDER.hasMatchingSignature(method);
}
private final Session session;
private final Object endpoint;
private final Method method;
private final DynamicArgs callable;
public OnTextFunction(Session session, Object endpoint, Method method)
{
this.session = session;
this.endpoint = endpoint;
this.method = method;
ReflectUtils.assertIsAnnotated(method,OnWebSocketMessage.class);
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method,Void.TYPE);
this.callable = ARGBUILDER.build(method);
if (this.callable == null)
{
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER);
}
this.callable.setArgReferences(SESSION,TEXT);
}
@Override
public Void apply(String text)
{
Object args[] = this.callable.toArgs(session,text);
try
{
method.invoke(endpoint,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(),method),e);
}
return null;
}
}

View File

@ -45,6 +45,7 @@ import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WebSocketPolicy.PolicyUpdate;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.api.extensions.Frame;
@ -58,7 +59,7 @@ import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
/**
* Provides the implementation of {@link LogicalConnection} within the framework of the new {@link org.eclipse.jetty.io.Connection} framework of {@code jetty-io}.
*/
public abstract class AbstractWebSocketConnection extends AbstractConnection implements LogicalConnection, Connection.UpgradeTo, ConnectionStateListener, Dumpable
public abstract class AbstractWebSocketConnection extends AbstractConnection implements LogicalConnection, Connection.UpgradeTo, ConnectionStateListener, PolicyUpdate, Dumpable
{
private final AtomicBoolean closed = new AtomicBoolean();
@ -244,6 +245,14 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
this.flusher = new Flusher(bufferPool,generator,endp);
this.setInputBufferSize(policy.getInputBufferSize());
this.setMaxIdleTimeout(policy.getIdleTimeout());
this.policy.addListener(this);
}
@Override
public void onPolicyUpdate(WebSocketPolicy policy)
{
this.setInputBufferSize(policy.getInputBufferSize());
this.setMaxIdleTimeout(policy.getIdleTimeout());
}
@Override
@ -434,6 +443,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
if (LOG.isDebugEnabled())
LOG.debug("{} onClose()",policy.getBehavior());
super.onClose();
policy.removeListener(this);
ioState.onDisconnected();
flusher.close();
}

View File

@ -0,0 +1,74 @@
//
// ========================================================================
// 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.common.message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.function.Function;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
public class ByteArrayMessageSink implements MessageSink
{
private static final int BUFFER_SIZE = 65535;
private final WebSocketPolicy policy;
private final Function<byte[], Void> onMessageFunction;
private ByteArrayOutputStream out;
private int size;
public ByteArrayMessageSink(WebSocketPolicy policy, Function<byte[], Void> onMessageFunction)
{
this.policy = policy;
this.onMessageFunction = onMessageFunction;
}
@Override
public void accept(ByteBuffer payload, Boolean fin)
{
try
{
if (payload != null)
{
policy.assertValidBinaryMessageSize(size + payload.remaining());
size += payload.remaining();
if (out == null)
out = new ByteArrayOutputStream(BUFFER_SIZE);
BufferUtil.writeTo(payload,out);
}
}
catch (IOException e)
{
throw new RuntimeException("Unable to append Binary Message", e);
}
finally
{
if (fin)
{
onMessageFunction.apply(out.toByteArray());
// reset
out = null;
size = 0;
}
}
}
}

View File

@ -0,0 +1,75 @@
//
// ========================================================================
// 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.common.message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.function.Function;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
public class ByteBufferMessageSink implements MessageSink
{
private static final int BUFFER_SIZE = 65535;
private final WebSocketPolicy policy;
private final Function<ByteBuffer, Void> onMessageFunction;
private ByteArrayOutputStream out;
private int size;
public ByteBufferMessageSink(WebSocketPolicy policy, Function<ByteBuffer, Void> onMessageFunction)
{
this.policy = policy;
this.onMessageFunction = onMessageFunction;
}
@Override
public void accept(ByteBuffer payload, Boolean fin)
{
try
{
if (payload != null)
{
policy.assertValidBinaryMessageSize(size + payload.remaining());
size += payload.remaining();
if (out == null)
out = new ByteArrayOutputStream(BUFFER_SIZE);
BufferUtil.writeTo(payload,out);
}
}
catch (IOException e)
{
throw new RuntimeException("Unable to append Binary Message", e);
}
finally
{
if (fin)
{
ByteBuffer bbuf = ByteBuffer.wrap(out.toByteArray());
onMessageFunction.apply(bbuf);
// reset
out = null;
size = 0;
}
}
}
}

View File

@ -0,0 +1,74 @@
//
// ========================================================================
// 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.common.message;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.function.Function;
public class InputStreamMessageSink implements MessageSink
{
private final Function<InputStream, Void> onStreamFunction;
private final Executor executor;
private MessageInputStream stream;
public InputStreamMessageSink(Executor executor, Function<InputStream, Void> function)
{
this.executor = executor;
this.onStreamFunction = function;
}
@Override
public void accept(ByteBuffer payload, Boolean fin)
{
try
{
boolean first = false;
if (stream == null)
{
stream = new MessageInputStream();
first = true;
}
stream.accept(payload,fin);
if (first)
{
executor.execute(new Runnable()
{
@Override
public void run()
{
// processing of errors is the responsibility
// of the stream function
onStreamFunction.apply(stream);
}
});
}
}
finally
{
if (fin)
{
stream = null;
}
}
}
}

View File

@ -35,7 +35,7 @@ import org.eclipse.jetty.util.log.Logger;
* <p>
* An InputStream that can access a queue of ByteBuffer payloads, along with expected InputStream blocking behavior.
*/
public class MessageInputStream extends InputStream implements MessageAppender
public class MessageInputStream extends InputStream implements MessageSink
{
private static final Logger LOG = Log.getLogger(MessageInputStream.class);
private static final ByteBuffer EOF = ByteBuffer.allocate(0).asReadOnlyBuffer();
@ -56,11 +56,11 @@ public class MessageInputStream extends InputStream implements MessageAppender
}
@Override
public void appendFrame(ByteBuffer framePayload, boolean fin) throws IOException
public void accept(ByteBuffer payload, Boolean fin)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Appending {} chunk: {}",fin?"final":"non-final",BufferUtil.toDetailString(framePayload));
LOG.debug("Appending {} chunk: {}",fin?"final":"non-final",BufferUtil.toDetailString(payload));
}
// If closed, we should just toss incoming payloads into the bit bucket.
@ -74,26 +74,26 @@ public class MessageInputStream extends InputStream implements MessageAppender
// be processed after this method returns.
try
{
if (framePayload == null)
if (payload == null)
{
// skip if no payload
return;
}
int capacity = framePayload.remaining();
int capacity = payload.remaining();
if (capacity <= 0)
{
// skip if no payload data to copy
return;
}
// TODO: the copy buffer should be pooled too, but no buffer pool available from here.
ByteBuffer copy = framePayload.isDirect()?ByteBuffer.allocateDirect(capacity):ByteBuffer.allocate(capacity);
copy.put(framePayload).flip();
ByteBuffer copy = payload.isDirect()?ByteBuffer.allocateDirect(capacity):ByteBuffer.allocate(capacity);
copy.put(payload).flip();
buffers.put(copy);
}
catch (InterruptedException e)
{
throw new IOException(e);
throw new RuntimeException(e);
}
finally
{
@ -126,14 +126,6 @@ public class MessageInputStream extends InputStream implements MessageAppender
return false;
}
@Override
public void messageComplete()
{
if (LOG.isDebugEnabled())
LOG.debug("Message completed");
buffers.offer(EOF);
}
@Override
public int read() throws IOException
{

View File

@ -18,18 +18,16 @@
package org.eclipse.jetty.websocket.common.message;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Support class for reading a (single) WebSocket TEXT message via a Reader.
* <p>
* In compliance to the WebSocket spec, this reader always uses the UTF8 {@link Charset}.
* In compliance to the WebSocket spec, this reader always uses the {@link StandardCharsets#UTF_8}.
*/
public class MessageReader extends InputStreamReader implements MessageAppender
public class MessageReader extends InputStreamReader implements MessageSink
{
private final MessageInputStream stream;
@ -40,14 +38,8 @@ public class MessageReader extends InputStreamReader implements MessageAppender
}
@Override
public void appendFrame(ByteBuffer payload, boolean isLast) throws IOException
public void accept(ByteBuffer payload, Boolean fin)
{
this.stream.appendFrame(payload, isLast);
}
@Override
public void messageComplete()
{
this.stream.messageComplete();
this.stream.accept(payload, fin);
}
}

View File

@ -18,30 +18,23 @@
package org.eclipse.jetty.websocket.common.message;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.function.BiConsumer;
/**
* Appender for messages (used for multiple frames with continuations, and also to allow for streaming APIs)
* Sink consumer for messages (used for multiple frames with continuations,
* and also to allow for streaming APIs)
*/
public interface MessageAppender
public interface MessageSink extends BiConsumer<ByteBuffer, Boolean>
{
/**
* Append the frame payload to the message.
* Consume the frame payload to the message.
*
* @param framePayload
* @param payload
* the frame payload to append.
* @param isLast
* flag indicating if this is the last part of the message or not.
* @throws IOException
* if unable to append the frame payload
* @param fin
* flag indicating if this is the final part of the message or not.
*/
abstract void appendFrame(ByteBuffer framePayload, boolean isLast) throws IOException;
/**
* Notification that message is to be considered complete.
* <p>
* Any cleanup or final actions should be taken here.
*/
abstract void messageComplete();
@Override
void accept(ByteBuffer payload, Boolean fin);
}

View File

@ -16,8 +16,32 @@
// ========================================================================
//
/**
* Jetty WebSocket Common : Event Driver for WebSocket Object
*/
package org.eclipse.jetty.websocket.common.events;
package org.eclipse.jetty.websocket.common.message;
import java.nio.ByteBuffer;
import java.util.function.Function;
/**
* {@link Function} argument for Partial Binary Messages
*/
public class PartialBinaryMessage
{
private final ByteBuffer payload;
private final boolean fin;
public PartialBinaryMessage(ByteBuffer payload, boolean fin)
{
this.payload = payload;
this.fin = fin;
}
public ByteBuffer getPayload()
{
return payload;
}
public boolean isFin()
{
return fin;
}
}

View File

@ -16,30 +16,23 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.common.events;
package org.eclipse.jetty.websocket.common.message;
import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import java.nio.ByteBuffer;
import java.util.function.Function;
public class JettyListenerImpl implements EventDriverImpl
public class PartialBinaryMessageSink implements MessageSink
{
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
private final Function<PartialBinaryMessage, Void> onBinaryFunction;
public PartialBinaryMessageSink(Function<PartialBinaryMessage, Void> function)
{
WebSocketConnectionListener listener = (WebSocketConnectionListener)websocket;
return new JettyListenerEventDriver(policy,listener);
this.onBinaryFunction = function;
}
@Override
public String describeRule()
public void accept(ByteBuffer payload, Boolean fin)
{
return "class implements " + WebSocketListener.class.getName();
}
@Override
public boolean supports(Object websocket)
{
return (websocket instanceof WebSocketConnectionListener);
onBinaryFunction.apply(new PartialBinaryMessage(payload,fin));
}
}

View File

@ -16,18 +16,31 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.common.events;
package org.eclipse.jetty.websocket.common.message;
import java.util.ArrayList;
import java.util.function.Function;
/**
* Simple class for representing a list of class arrays.
* {@link Function} argument for Partial Text Messages
*/
@SuppressWarnings("serial")
public class ParamList extends ArrayList<Class<?>[]>
public class PartialTextMessage
{
public void addParams(Class<?>... paramTypes)
private final String payload;
private final boolean fin;
public PartialTextMessage(String payload, boolean fin)
{
this.add(paramTypes);
this.payload = payload;
this.fin = fin;
}
public String getPayload()
{
return payload;
}
public boolean isFin()
{
return fin;
}
}

Some files were not shown because too many files have changed in this diff Show More