parent
8de4751c6f
commit
39f1d7332c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<>();
|
||||
|
|
|
@ -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>>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<byte[]> 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 */
|
||||
}
|
||||
}
|
|
@ -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<ByteBuffer> 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 */
|
||||
}
|
||||
}
|
|
@ -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 */
|
||||
}
|
||||
}
|
|
@ -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 @{@link OnMessage} annotated methods
|
||||
*/
|
||||
public class BinaryPartialOnMessage implements MessageAppender
|
||||
public class BinaryPartialOnMessage implements MessageSink
|
||||
{
|
||||
private final JsrAnnotatedEventDriver driver;
|
||||
private boolean finished;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @{@link OnMessage} annotated methods
|
||||
*/
|
||||
public class TextPartialOnMessage implements MessageAppender
|
||||
public class TextPartialOnMessage implements MessageSink
|
||||
{
|
||||
private final JsrAnnotatedEventDriver driver;
|
||||
private final Utf8PartialBuilder utf8Partial;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -27,6 +27,5 @@ public class ByteBufferPartialHandler implements MessageHandler.Partial<ByteBuff
|
|||
@Override
|
||||
public void onMessage(ByteBuffer partialMessage, boolean last)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -103,7 +103,6 @@ public class WebSocketClientSelectorManager extends SelectorManager
|
|||
else
|
||||
{
|
||||
// Standard "ws://"
|
||||
endPoint.setIdleTimeout(connectPromise.getDriver().getPolicy().getIdleTimeout());
|
||||
return newUpgradeConnection(channel,endPoint,connectPromise);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 @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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/** @OnWebSocketConnect () */
|
||||
public CallableMethod onConnect;
|
||||
/** @OnWebSocketMessage (byte[], or ByteBuffer, or InputStream) */
|
||||
public OptionalSessionCallableMethod onBinary;
|
||||
/** @OnWebSocketMessage (String, or Reader) */
|
||||
public OptionalSessionCallableMethod onText;
|
||||
/** @OnWebSocketFrame (Frame) */
|
||||
public OptionalSessionCallableMethod onFrame;
|
||||
/** @OnWebSocketError (Throwable) */
|
||||
public OptionalSessionCallableMethod onError;
|
||||
/** @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();
|
||||
}
|
||||
}
|
|
@ -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 @OnWebSocketMessage (Binary mode)
|
||||
*/
|
||||
private static final ParamList validBinaryParams;
|
||||
|
||||
/**
|
||||
* Parameter list for @OnWebSocketConnect
|
||||
*/
|
||||
private static final ParamList validConnectParams;
|
||||
|
||||
/**
|
||||
* Parameter list for @OnWebSocketClose
|
||||
*/
|
||||
private static final ParamList validCloseParams;
|
||||
|
||||
/**
|
||||
* Parameter list for @OnWebSocketError
|
||||
*/
|
||||
private static final ParamList validErrorParams;
|
||||
|
||||
/**
|
||||
* Parameter list for @OnWebSocketFrame
|
||||
*/
|
||||
private static final ParamList validFrameParams;
|
||||
|
||||
/**
|
||||
* Parameter list for @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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue