parent
3198d12127
commit
57bbf67735
|
@ -20,10 +20,12 @@ package org.eclipse.jetty.websocket.jsr356;
|
|||
|
||||
import javax.websocket.EndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.ManagedEndpoint;
|
||||
|
||||
/**
|
||||
* Associate a JSR Endpoint with its optional {@link EndpointConfig}
|
||||
*/
|
||||
public class ConfiguredEndpoint
|
||||
public class ConfiguredEndpoint implements ManagedEndpoint
|
||||
{
|
||||
/** The instance of the Endpoint */
|
||||
private final Object endpoint;
|
||||
|
@ -45,4 +47,10 @@ public class ConfiguredEndpoint
|
|||
{
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRawEndpoint()
|
||||
{
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
|
|||
* <li>Container declared DecoderMetadataSet (primitives)</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Deprecated
|
||||
public class DecoderFactory implements Configurable
|
||||
{
|
||||
public static class Wrapper implements Configurable
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
|
|||
/**
|
||||
* Represents all of the declared {@link Encoder}s that the Container is aware of.
|
||||
*/
|
||||
@Deprecated
|
||||
public class EncoderFactory implements Configurable
|
||||
{
|
||||
public static class Wrapper implements Configurable
|
||||
|
|
|
@ -1,342 +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;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.PongMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
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.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* Used to identify Endpoint Functions
|
||||
*/
|
||||
public class EndpointFunctions
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(EndpointFunctions.class);
|
||||
|
||||
public enum Role
|
||||
{
|
||||
OPEN,
|
||||
CLOSE,
|
||||
ERROR,
|
||||
TEXT,
|
||||
TEXT_STREAM,
|
||||
BINARY,
|
||||
BINARY_STREAM,
|
||||
PONG
|
||||
}
|
||||
|
||||
public class ArgRole
|
||||
{
|
||||
public Role role;
|
||||
public DynamicArgs.Builder argBuilder;
|
||||
public Predicate<Method> predicate;
|
||||
}
|
||||
|
||||
private final Map<String, String> uriParams;
|
||||
private final ArgRole[] argRoles;
|
||||
|
||||
public EndpointFunctions()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
public EndpointFunctions(Map<String, String> uriParams)
|
||||
{
|
||||
this.uriParams = uriParams;
|
||||
|
||||
argRoles = new ArgRole[Role.values().length + 1];
|
||||
int argRoleIdx = 0;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnOpen
|
||||
ArgRole onOpen = new ArgRole();
|
||||
onOpen.role = Role.OPEN;
|
||||
onOpen.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class)
|
||||
);
|
||||
onOpen.predicate = (method) ->
|
||||
hasAnnotation(method, OnOpen.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
isReturnType(method, Void.TYPE) &&
|
||||
onOpen.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onOpen;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnClose
|
||||
ArgRole onClose = new ArgRole();
|
||||
onClose.role = Role.CLOSE;
|
||||
onClose.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(CloseReason.class) // close
|
||||
);
|
||||
onClose.predicate = (method) ->
|
||||
hasAnnotation(method, OnClose.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
isReturnType(method, Void.TYPE) &&
|
||||
onClose.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onClose;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnError
|
||||
ArgRole onError = new ArgRole();
|
||||
onError.role = Role.ERROR;
|
||||
onError.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(Throwable.class) // cause
|
||||
);
|
||||
onError.predicate = (method) ->
|
||||
hasAnnotation(method, OnError.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
isReturnType(method, Void.TYPE) &&
|
||||
onError.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onError;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnMessage / Text (whole message)
|
||||
ArgRole onText = new ArgRole();
|
||||
onText.role = Role.TEXT;
|
||||
onText.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(String.class) // message
|
||||
);
|
||||
onText.predicate = (method) ->
|
||||
hasAnnotation(method, OnMessage.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
hasSupportedReturnType(method) &&
|
||||
onText.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onText;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnMessage / Binary (whole message, byte array)
|
||||
ArgRole onBinaryArray = new ArgRole();
|
||||
onBinaryArray.role = Role.BINARY;
|
||||
onBinaryArray.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(byte[].class), // buffer
|
||||
new Arg(int.class), // length
|
||||
new Arg(int.class) // offset
|
||||
);
|
||||
onBinaryArray.predicate = (method) ->
|
||||
hasAnnotation(method, OnMessage.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
hasSupportedReturnType(method) &&
|
||||
onBinaryArray.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onBinaryArray;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnMessage / Binary (whole message, ByteBuffer)
|
||||
ArgRole onBinaryBuffer = new ArgRole();
|
||||
onBinaryBuffer.role = Role.BINARY;
|
||||
onBinaryBuffer.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(ByteBuffer.class) // buffer
|
||||
);
|
||||
onBinaryBuffer.predicate = (method) ->
|
||||
hasAnnotation(method, OnMessage.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
hasSupportedReturnType(method) &&
|
||||
onBinaryBuffer.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onBinaryBuffer;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnMessage / Text (streamed)
|
||||
ArgRole onTextStream = new ArgRole();
|
||||
onTextStream.role = Role.TEXT_STREAM;
|
||||
onTextStream.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(Reader.class) // stream
|
||||
);
|
||||
onTextStream.predicate = (method) ->
|
||||
hasAnnotation(method, OnMessage.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
hasSupportedReturnType(method) &&
|
||||
onTextStream.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onTextStream;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnMessage / Binary (streamed)
|
||||
ArgRole onBinaryStream = new ArgRole();
|
||||
onBinaryStream.role = Role.BINARY_STREAM;
|
||||
onBinaryStream.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(InputStream.class) // stream
|
||||
);
|
||||
onBinaryStream.predicate = (method) ->
|
||||
hasAnnotation(method, OnMessage.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
hasSupportedReturnType(method) &&
|
||||
onBinaryStream.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onBinaryStream;
|
||||
|
||||
// ------------------------------------------------
|
||||
// @OnMessage / Pong
|
||||
ArgRole onPong = new ArgRole();
|
||||
onPong.role = Role.PONG;
|
||||
onPong.argBuilder = newDynamicBuilder(
|
||||
new Arg(Session.class),
|
||||
new Arg(PongMessage.class) // payload
|
||||
);
|
||||
onPong.predicate = (method) ->
|
||||
hasAnnotation(method, OnMessage.class) &&
|
||||
isPublicNonStatic(method) &&
|
||||
isReturnType(method, Void.TYPE) &&
|
||||
onPong.argBuilder.hasMatchingSignature(method);
|
||||
argRoles[argRoleIdx++] = onPong;
|
||||
}
|
||||
|
||||
private DynamicArgs.Builder newDynamicBuilder(Arg... args)
|
||||
{
|
||||
DynamicArgs.Builder argBuilder = new DynamicArgs.Builder();
|
||||
int argCount = args.length;
|
||||
if (this.uriParams != null)
|
||||
argCount += uriParams.size();
|
||||
|
||||
Arg[] callArgs = new Arg[argCount];
|
||||
int idx = 0;
|
||||
for (Arg arg : args)
|
||||
{
|
||||
callArgs[idx++] = arg;
|
||||
}
|
||||
|
||||
if (this.uriParams != null)
|
||||
{
|
||||
for (Map.Entry<String, String> uriParam : uriParams.entrySet())
|
||||
{
|
||||
// TODO: translate from UriParam String to method param type?
|
||||
// TODO: use decoder?
|
||||
callArgs[idx++] = new Arg(uriParam.getValue().getClass()).setTag(uriParam.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
argBuilder.addSignature(new UnorderedSignature(callArgs));
|
||||
|
||||
return argBuilder;
|
||||
}
|
||||
|
||||
private static boolean hasAnnotation(Method method, Class<? extends Annotation> annoClass)
|
||||
{
|
||||
return (method.getAnnotation(annoClass) != null);
|
||||
}
|
||||
|
||||
private static boolean isReturnType(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);
|
||||
LOG.warn(err.toString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasSupportedReturnType(Method method)
|
||||
{
|
||||
// TODO: check Encoder list
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPublicNonStatic(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");
|
||||
LOG.warn(err.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Modifier.isStatic(mods))
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Invalid declaration of ");
|
||||
err.append(method);
|
||||
err.append(System.lineSeparator());
|
||||
err.append("Method modifier must not be static");
|
||||
LOG.warn(err.toString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ArgRole getArgRole(Method method, Class<? extends Annotation> annoClass, Role role)
|
||||
{
|
||||
ArgRole ret = null;
|
||||
|
||||
for (ArgRole argRole : this.argRoles)
|
||||
{
|
||||
if ((argRole.role == role) && (argRole.predicate.test(method)))
|
||||
{
|
||||
ret = argRole;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == null)
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Invalid declaration of @");
|
||||
err.append(annoClass.getSimpleName());
|
||||
err.append(" method ");
|
||||
err.append(method);
|
||||
throw new InvalidSignatureException(err.toString());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ArgRole findArgRole(Method method)
|
||||
{
|
||||
for (ArgRole argRole : this.argRoles)
|
||||
{
|
||||
if (argRole.predicate.test(method))
|
||||
{
|
||||
return argRole;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -85,7 +85,7 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
|
||||
this.container = container;
|
||||
|
||||
ConfiguredEndpoint cendpoint = (ConfiguredEndpoint)websocket;
|
||||
ConfiguredEndpoint cendpoint = (ConfiguredEndpoint) websocket;
|
||||
this.config = cendpoint.getConfig();
|
||||
|
||||
DecoderMetadataSet decoderSet = new DecoderMetadataSet();
|
||||
|
@ -93,14 +93,14 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
// TODO: figure out how to populate the decoderSet / encoderSet
|
||||
|
||||
this.id = id;
|
||||
this.decoderFactory = new DecoderFactory(this,decoderSet,container.getDecoderFactory());
|
||||
this.encoderFactory = new EncoderFactory(this,encoderSet,container.getEncoderFactory());
|
||||
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)
|
||||
if (!(obj instanceof ConfiguredEndpoint))
|
||||
{
|
||||
throw new IllegalArgumentException("JSR356 Implementation expects a " + ConfiguredEndpoint.class.getName() + " but got: " + obj.getClass().getName());
|
||||
}
|
||||
|
@ -110,21 +110,21 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
// Endpoint
|
||||
Object websocket = cendpoint.getEndpoint();
|
||||
|
||||
if(websocket instanceof Endpoint)
|
||||
if (websocket instanceof Endpoint)
|
||||
{
|
||||
Endpoint endpoint = (Endpoint)websocket;
|
||||
Endpoint endpoint = (Endpoint) websocket;
|
||||
onOpenFunction = (sess) -> {
|
||||
endpoint.onOpen(this,config);
|
||||
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);
|
||||
CloseReason closeReason = new CloseReason(closeCode, closeinfo.getReason());
|
||||
endpoint.onClose(this, closeReason);
|
||||
return null;
|
||||
};
|
||||
onErrorFunction = (cause) -> {
|
||||
endpoint.onError(this,cause);
|
||||
endpoint.onError(this, cause);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
|
||||
Class<?> websocketClass = websocket.getClass();
|
||||
ClientEndpoint clientEndpoint = websocketClass.getAnnotation(ClientEndpoint.class);
|
||||
if(clientEndpoint != null)
|
||||
if (clientEndpoint != null)
|
||||
{
|
||||
/*Method onmethod = null;
|
||||
|
||||
|
@ -180,27 +180,27 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
Objects.requireNonNull(handler, "MessageHandler.Partial cannot be null");
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("MessageHandler.Partial class: {}",handler.getClass());
|
||||
LOG.debug("MessageHandler.Partial class: {}", handler.getClass());
|
||||
}
|
||||
|
||||
// No decoders for Partial messages per JSR-356 (PFD1 spec)
|
||||
|
||||
if(String.class.isAssignableFrom(clazz))
|
||||
if (String.class.isAssignableFrom(clazz))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Partial<String> strhandler = (Partial<String>)handler;
|
||||
Partial<String> strhandler = (Partial<String>) handler;
|
||||
setMessageAppender(MessageType.TEXT, new TextPartialMessage(strhandler));
|
||||
}
|
||||
else if(ByteBuffer.class.isAssignableFrom(clazz))
|
||||
else if (ByteBuffer.class.isAssignableFrom(clazz))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Partial<ByteBuffer> bufhandler = (Partial<ByteBuffer>)handler;
|
||||
Partial<ByteBuffer> bufhandler = (Partial<ByteBuffer>) handler;
|
||||
// setMessageAppender(MessageType.BINARY, new BinaryBufferPartialMessage(bufhandler));
|
||||
}
|
||||
else if(byte[].class.isAssignableFrom(clazz))
|
||||
else if (byte[].class.isAssignableFrom(clazz))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Partial<byte[]> arrhandler = (Partial<byte[]>)handler;
|
||||
Partial<byte[]> arrhandler = (Partial<byte[]>) handler;
|
||||
// setMessageAppender(MessageType.BINARY, new BinaryArrayPartialMessage(arrhandler));
|
||||
}
|
||||
else
|
||||
|
@ -218,7 +218,7 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
Objects.requireNonNull(handler, "MessageHandler.Whole cannot be null");
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("MessageHandler.Whole class: {}",handler.getClass());
|
||||
LOG.debug("MessageHandler.Whole class: {}", handler.getClass());
|
||||
}
|
||||
|
||||
// Determine Decoder
|
||||
|
@ -234,45 +234,45 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
throw new IllegalStateException(err.toString());
|
||||
}
|
||||
|
||||
if(decoderWrapper.getMetadata().isStreamed())
|
||||
if (decoderWrapper.getMetadata().isStreamed())
|
||||
{
|
||||
// Streaming
|
||||
if(InputStream.class.isAssignableFrom(clazz))
|
||||
if (InputStream.class.isAssignableFrom(clazz))
|
||||
{
|
||||
// Whole Text Streaming
|
||||
@SuppressWarnings("unchecked")
|
||||
Whole<Object> streamhandler = (Whole<Object>)handler;
|
||||
Decoder.BinaryStream<?> streamdecoder = (Decoder.BinaryStream<?>)decoderWrapper.getDecoder();
|
||||
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))
|
||||
}
|
||||
else if (Reader.class.isAssignableFrom(clazz))
|
||||
{
|
||||
// Whole Reader Streaming
|
||||
@SuppressWarnings("unchecked")
|
||||
Whole<Object> streamhandler = (Whole<Object>)handler;
|
||||
Decoder.TextStream<?> streamdecoder = (Decoder.TextStream<?>)decoderWrapper.getDecoder();
|
||||
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" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public void addMessageHandler(MessageHandler handler) throws IllegalStateException
|
||||
{
|
||||
Objects.requireNonNull(handler, "MessageHandler cannot be null");
|
||||
Class<? extends MessageHandler> handlerClass = handler.getClass();
|
||||
Class<? extends MessageHandler> handlerClass = handler.getClass();
|
||||
|
||||
if (MessageHandler.Whole.class.isAssignableFrom(handlerClass))
|
||||
{
|
||||
Class<?> onMessageClass = ReflectUtils.findGenericClassFor(handlerClass,MessageHandler.Whole.class);
|
||||
addMessageHandler(onMessageClass,(Whole)handler);
|
||||
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);
|
||||
Class<?> onMessageClass = ReflectUtils.findGenericClassFor(handlerClass, MessageHandler.Partial.class);
|
||||
addMessageHandler(onMessageClass, (Partial) handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,7 +361,7 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
@Override
|
||||
public void close(CloseReason closeReason) throws IOException
|
||||
{
|
||||
close(closeReason.getCloseCode().getCode(),closeReason.getReasonPhrase());
|
||||
close(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -589,5 +589,4 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
|
|||
// JSR 356 specification mandates default batch mode to be off.
|
||||
return BatchMode.OFF;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,5 +27,5 @@ public enum MessageType
|
|||
{
|
||||
TEXT,
|
||||
BINARY,
|
||||
PONG;
|
||||
PONG
|
||||
}
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.decoders;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Decoder;
|
||||
import javax.websocket.PongMessage;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
public class AvailableDecoders implements Predicate<Class<?>>
|
||||
{
|
||||
private static class RegisteredDecoder implements Predicate<Class<?>>
|
||||
{
|
||||
public final Class<? extends Decoder> decoder;
|
||||
public final Class<?> objectType;
|
||||
|
||||
public RegisteredDecoder(Class<? extends Decoder> decoder, Class<?> objectType)
|
||||
{
|
||||
this.decoder = decoder;
|
||||
this.objectType = objectType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Class<?> type)
|
||||
{
|
||||
return objectType.isAssignableFrom(type);
|
||||
}
|
||||
}
|
||||
|
||||
private List<RegisteredDecoder> registeredDecoders;
|
||||
|
||||
public void register(Class<? extends Decoder> decoder)
|
||||
{
|
||||
if (!ReflectUtils.isDefaultConstructable(decoder))
|
||||
{
|
||||
throw new InvalidSignatureException("Decoder must have public, no-args constructor: " + decoder.getName());
|
||||
}
|
||||
|
||||
boolean foundDecoder = false;
|
||||
|
||||
if (Decoder.Binary.class.isAssignableFrom(decoder))
|
||||
{
|
||||
add(decoder, Decoder.Binary.class);
|
||||
foundDecoder = true;
|
||||
}
|
||||
|
||||
if (Decoder.BinaryStream.class.isAssignableFrom(decoder))
|
||||
{
|
||||
add(decoder, Decoder.BinaryStream.class);
|
||||
foundDecoder = true;
|
||||
}
|
||||
|
||||
if (Decoder.Text.class.isAssignableFrom(decoder))
|
||||
{
|
||||
add(decoder, Decoder.Text.class);
|
||||
foundDecoder = true;
|
||||
}
|
||||
|
||||
if (Decoder.TextStream.class.isAssignableFrom(decoder))
|
||||
{
|
||||
add(decoder, Decoder.TextStream.class);
|
||||
foundDecoder = true;
|
||||
}
|
||||
|
||||
if (!foundDecoder)
|
||||
{
|
||||
throw new InvalidSignatureException("Not a valid Decoder class: " + decoder.getName() + " implements no " + Decoder.class.getName() + " interfaces");
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAll(Class<? extends Decoder>[] decoders)
|
||||
{
|
||||
if (decoders == null)
|
||||
return;
|
||||
|
||||
for (Class<? extends Decoder> decoder : decoders)
|
||||
{
|
||||
register(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAll(List<Class<? extends Decoder>> decoders)
|
||||
{
|
||||
if (decoders == null)
|
||||
return;
|
||||
|
||||
decoders.forEach(this::register);
|
||||
}
|
||||
|
||||
private void add(Class<? extends Decoder> decoder, Class<?> interfaceClass)
|
||||
{
|
||||
Class<?> objectType = ReflectUtils.findGenericClassFor(decoder, interfaceClass);
|
||||
if (objectType == null)
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Invalid Decoder Object type declared for interface ");
|
||||
err.append(interfaceClass.getName());
|
||||
err.append(" on class ");
|
||||
err.append(decoder);
|
||||
throw new InvalidWebSocketException(err.toString());
|
||||
}
|
||||
|
||||
if (registeredDecoders == null)
|
||||
registeredDecoders = new ArrayList<>();
|
||||
|
||||
registeredDecoders.add(new RegisteredDecoder(decoder, objectType));
|
||||
}
|
||||
|
||||
public Class<? extends Decoder> getDecoderFor(Class<?> type)
|
||||
{
|
||||
// Check registered decoders first
|
||||
if (registeredDecoders != null)
|
||||
{
|
||||
for (RegisteredDecoder registered : registeredDecoders)
|
||||
{
|
||||
if (registered.objectType.isAssignableFrom(type))
|
||||
return registered.decoder;
|
||||
}
|
||||
}
|
||||
|
||||
// Check default decoders next
|
||||
|
||||
// TEXT based [via Class reference]
|
||||
if (Boolean.class.isAssignableFrom(type)) return BooleanDecoder.class;
|
||||
if (Byte.class.isAssignableFrom(type)) return ByteDecoder.class;
|
||||
if (Character.class.isAssignableFrom(type)) return CharacterDecoder.class;
|
||||
if (Double.class.isAssignableFrom(type)) return DoubleDecoder.class;
|
||||
if (Float.class.isAssignableFrom(type)) return FloatDecoder.class;
|
||||
if (Integer.class.isAssignableFrom(type)) return IntegerDecoder.class;
|
||||
if (Long.class.isAssignableFrom(type)) return LongDecoder.class;
|
||||
if (String.class.isAssignableFrom(type)) return StringDecoder.class;
|
||||
|
||||
// TEXT based [via Primitive reference]
|
||||
if (Boolean.TYPE.isAssignableFrom(type)) return BooleanDecoder.class;
|
||||
if (Byte.TYPE.isAssignableFrom(type)) return ByteDecoder.class;
|
||||
if (Character.TYPE.isAssignableFrom(type)) return CharacterDecoder.class;
|
||||
if (Double.TYPE.isAssignableFrom(type)) return DoubleDecoder.class;
|
||||
if (Float.TYPE.isAssignableFrom(type)) return FloatDecoder.class;
|
||||
if (Integer.TYPE.isAssignableFrom(type)) return IntegerDecoder.class;
|
||||
if (Long.TYPE.isAssignableFrom(type)) return LongDecoder.class;
|
||||
|
||||
// BINARY based
|
||||
if (ByteBuffer.class.isAssignableFrom(type)) return ByteBufferDecoder.class;
|
||||
if (byte[].class.isAssignableFrom(type)) return ByteArrayDecoder.class;
|
||||
|
||||
// PONG based
|
||||
if (PongMessage.class.isAssignableFrom(type)) return PongMessageDecoder.class;
|
||||
|
||||
// STREAMING based
|
||||
if (Reader.class.isAssignableFrom(type)) return ReaderDecoder.class;
|
||||
if (InputStream.class.isAssignableFrom(type)) return InputStreamDecoder.class;
|
||||
|
||||
throw new InvalidWebSocketException("No Decoder found for type " + type);
|
||||
}
|
||||
|
||||
public static Object decodePrimitive(String value, Class<?> type) throws DecodeException
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// Simplest (and most common) form of @PathParam
|
||||
if (String.class.isAssignableFrom(type))
|
||||
return value;
|
||||
|
||||
try
|
||||
{
|
||||
// Per JSR356 spec, just the java primitives
|
||||
if (Boolean.class.isAssignableFrom(type))
|
||||
{
|
||||
return new Boolean(value);
|
||||
}
|
||||
if (Boolean.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
if (Byte.class.isAssignableFrom(type))
|
||||
{
|
||||
return new Byte(value);
|
||||
}
|
||||
if (Byte.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return Byte.parseByte(value);
|
||||
}
|
||||
if (Character.class.isAssignableFrom(type))
|
||||
{
|
||||
if (value.length() != 1)
|
||||
throw new DecodeException(value, "Invalid Size: Cannot decode as type " + Character.class.getName());
|
||||
return new Character(value.charAt(0));
|
||||
}
|
||||
if (Character.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
if (value.length() != 1)
|
||||
throw new DecodeException(value, "Invalid Size: Cannot decode as type " + Character.class.getName());
|
||||
return value.charAt(0);
|
||||
}
|
||||
if (Double.class.isAssignableFrom(type))
|
||||
{
|
||||
return new Double(value);
|
||||
}
|
||||
if (Double.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
if (Float.class.isAssignableFrom(type))
|
||||
{
|
||||
return new Float(value);
|
||||
}
|
||||
if (Float.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return Float.parseFloat(value);
|
||||
}
|
||||
if (Integer.class.isAssignableFrom(type))
|
||||
{
|
||||
return new Integer(value);
|
||||
}
|
||||
if (Integer.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
if (Long.class.isAssignableFrom(type))
|
||||
{
|
||||
return new Long(value);
|
||||
}
|
||||
if (Long.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
// Not a primitive!
|
||||
throw new DecodeException(value, "Not a recognized primitive type: " + type);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new DecodeException(value, "Unable to decode as type " + type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Class<?> type)
|
||||
{
|
||||
if (registeredDecoders != null)
|
||||
{
|
||||
for (RegisteredDecoder registered : registeredDecoders)
|
||||
{
|
||||
if (registered.test(type))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TEXT based [via Class references]
|
||||
if (Boolean.class.isAssignableFrom(type) ||
|
||||
Byte.class.isAssignableFrom(type) ||
|
||||
Character.class.isAssignableFrom(type) ||
|
||||
Double.class.isAssignableFrom(type) ||
|
||||
Float.class.isAssignableFrom(type) ||
|
||||
Integer.class.isAssignableFrom(type) ||
|
||||
Long.class.isAssignableFrom(type) ||
|
||||
String.class.isAssignableFrom(type) ||
|
||||
Reader.class.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TEXT based [via Primitive reference]
|
||||
if (Boolean.TYPE.isAssignableFrom(type) ||
|
||||
Byte.TYPE.isAssignableFrom(type) ||
|
||||
Character.TYPE.isAssignableFrom(type) ||
|
||||
Double.TYPE.isAssignableFrom(type) ||
|
||||
Float.TYPE.isAssignableFrom(type) ||
|
||||
Integer.TYPE.isAssignableFrom(type) ||
|
||||
Long.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// BINARY based
|
||||
if (ByteBuffer.class.isAssignableFrom(type) ||
|
||||
byte[].class.isAssignableFrom(type) ||
|
||||
InputStream.class.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// PONG based
|
||||
if (PongMessage.class.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.encoders;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.websocket.Encoder;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
public class AvailableEncoders implements Predicate<Class<?>>
|
||||
{
|
||||
private static class RegisteredEncoder implements Predicate<Class<?>>
|
||||
{
|
||||
public final Class<? extends Encoder> encoder;
|
||||
public final Class<?> objectType;
|
||||
|
||||
public RegisteredEncoder(Class<? extends Encoder> encoder, Class<?> objectType)
|
||||
{
|
||||
this.encoder = encoder;
|
||||
this.objectType = objectType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Class<?> type)
|
||||
{
|
||||
return objectType.isAssignableFrom(type);
|
||||
}
|
||||
}
|
||||
|
||||
private List<RegisteredEncoder> registeredEncoders;
|
||||
|
||||
public void register(Class<? extends Encoder> encoder)
|
||||
{
|
||||
if (!ReflectUtils.isDefaultConstructable(encoder))
|
||||
{
|
||||
throw new InvalidSignatureException("Encoder must have public, no-args constructor: " + encoder.getName());
|
||||
}
|
||||
|
||||
boolean foundEncoder = false;
|
||||
|
||||
if (Encoder.Binary.class.isAssignableFrom(encoder))
|
||||
{
|
||||
add(encoder, Encoder.Binary.class);
|
||||
foundEncoder = true;
|
||||
}
|
||||
|
||||
if (Encoder.BinaryStream.class.isAssignableFrom(encoder))
|
||||
{
|
||||
add(encoder, Encoder.BinaryStream.class);
|
||||
foundEncoder = true;
|
||||
}
|
||||
|
||||
if (Encoder.Text.class.isAssignableFrom(encoder))
|
||||
{
|
||||
add(encoder, Encoder.Text.class);
|
||||
foundEncoder = true;
|
||||
}
|
||||
|
||||
if (Encoder.TextStream.class.isAssignableFrom(encoder))
|
||||
{
|
||||
add(encoder, Encoder.TextStream.class);
|
||||
foundEncoder = true;
|
||||
}
|
||||
|
||||
if (!foundEncoder)
|
||||
{
|
||||
throw new InvalidSignatureException("Not a valid Encoder class: " + encoder.getName() + " implements no " + Encoder.class.getName() + " interfaces");
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAll(Class<? extends Encoder>[] encoders)
|
||||
{
|
||||
if (encoders == null)
|
||||
return;
|
||||
|
||||
for (Class<? extends Encoder> encoder : encoders)
|
||||
{
|
||||
register(encoder);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAll(List<Class<? extends Encoder>> encoders)
|
||||
{
|
||||
if (encoders == null)
|
||||
return;
|
||||
|
||||
encoders.forEach(this::register);
|
||||
}
|
||||
|
||||
private void add(Class<? extends Encoder> encoder, Class<?> interfaceClass)
|
||||
{
|
||||
Class<?> objectType = ReflectUtils.findGenericClassFor(encoder, interfaceClass);
|
||||
if (objectType == null)
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Invalid Encoder Object type declared for interface ");
|
||||
err.append(interfaceClass.getName());
|
||||
err.append(" on class ");
|
||||
err.append(encoder);
|
||||
throw new InvalidWebSocketException(err.toString());
|
||||
}
|
||||
|
||||
if (registeredEncoders == null)
|
||||
registeredEncoders = new ArrayList<>();
|
||||
|
||||
registeredEncoders.add(new RegisteredEncoder(encoder, objectType));
|
||||
}
|
||||
|
||||
public Class<? extends Encoder> getEncoderFor(Class<?> type)
|
||||
{
|
||||
// Check registered encoders first
|
||||
if (registeredEncoders != null)
|
||||
{
|
||||
for (RegisteredEncoder registered : registeredEncoders)
|
||||
{
|
||||
if (registered.objectType.isAssignableFrom(type))
|
||||
return registered.encoder;
|
||||
}
|
||||
}
|
||||
|
||||
// Check default encoders next
|
||||
|
||||
// TEXT based [via Class reference]
|
||||
if (Boolean.class.isAssignableFrom(type)) return BooleanEncoder.class;
|
||||
if (Byte.class.isAssignableFrom(type)) return ByteEncoder.class;
|
||||
if (Character.class.isAssignableFrom(type)) return CharacterEncoder.class;
|
||||
if (Double.class.isAssignableFrom(type)) return DoubleEncoder.class;
|
||||
if (Float.class.isAssignableFrom(type)) return FloatEncoder.class;
|
||||
if (Integer.class.isAssignableFrom(type)) return IntegerEncoder.class;
|
||||
if (Long.class.isAssignableFrom(type)) return LongEncoder.class;
|
||||
if (String.class.isAssignableFrom(type)) return StringEncoder.class;
|
||||
|
||||
// TEXT based [via Primitive reference]
|
||||
if (Boolean.TYPE.isAssignableFrom(type)) return BooleanEncoder.class;
|
||||
if (Byte.TYPE.isAssignableFrom(type)) return ByteEncoder.class;
|
||||
if (Character.TYPE.isAssignableFrom(type)) return CharacterEncoder.class;
|
||||
if (Double.TYPE.isAssignableFrom(type)) return DoubleEncoder.class;
|
||||
if (Float.TYPE.isAssignableFrom(type)) return FloatEncoder.class;
|
||||
if (Integer.TYPE.isAssignableFrom(type)) return IntegerEncoder.class;
|
||||
if (Long.TYPE.isAssignableFrom(type)) return LongEncoder.class;
|
||||
|
||||
// BINARY based
|
||||
if (ByteBuffer.class.isAssignableFrom(type)) return ByteBufferEncoder.class;
|
||||
if (byte[].class.isAssignableFrom(type)) return ByteArrayEncoder.class;
|
||||
|
||||
// Note: Streams (Writer / OutputStream) are not present here
|
||||
// as you don't write with a Stream via an encoder, you tell the
|
||||
// encoder to write an object to a Stream
|
||||
|
||||
throw new InvalidWebSocketException("No Encoder found for type " + type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Class<?> type)
|
||||
{
|
||||
if (registeredEncoders != null)
|
||||
{
|
||||
for (RegisteredEncoder registered : registeredEncoders)
|
||||
{
|
||||
if (registered.test(type))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TEXT based [via Class references]
|
||||
if (Boolean.class.isAssignableFrom(type) ||
|
||||
Byte.class.isAssignableFrom(type) ||
|
||||
Character.class.isAssignableFrom(type) ||
|
||||
Double.class.isAssignableFrom(type) ||
|
||||
Float.class.isAssignableFrom(type) ||
|
||||
Integer.class.isAssignableFrom(type) ||
|
||||
Long.class.isAssignableFrom(type) ||
|
||||
String.class.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TEXT based [via Primitive reference]
|
||||
if (Boolean.TYPE.isAssignableFrom(type) ||
|
||||
Byte.TYPE.isAssignableFrom(type) ||
|
||||
Character.TYPE.isAssignableFrom(type) ||
|
||||
Double.TYPE.isAssignableFrom(type) ||
|
||||
Float.TYPE.isAssignableFrom(type) ||
|
||||
Integer.TYPE.isAssignableFrom(type) ||
|
||||
Long.TYPE.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// BINARY based
|
||||
if (ByteBuffer.class.isAssignableFrom(type) ||
|
||||
byte[].class.isAssignableFrom(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import java.nio.ByteBuffer;
|
|||
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
|
||||
|
||||
@Deprecated
|
||||
public class PrimitiveEncoderMetadataSet extends EncoderMetadataSet
|
||||
{
|
||||
public static final EncoderMetadataSet INSTANCE = new PrimitiveEncoderMetadataSet();
|
||||
|
|
|
@ -0,0 +1,427 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Encoder;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.Session;
|
||||
|
||||
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.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.function.CommonEndpointFunctions;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.UnorderedSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.jsr356.JsrSession;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
|
||||
/**
|
||||
* Endpoint Functions used as interface between from the parsed websocket frames
|
||||
* and the user provided endpoint methods.
|
||||
*/
|
||||
public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(JsrEndpointFunctions.class);
|
||||
|
||||
/**
|
||||
* Represents a static value (as seen from a URI PathParam)
|
||||
* <p>
|
||||
* The decoding of the raw String to a object occurs later,
|
||||
* when the callable/sink/function is created for a method
|
||||
* that needs it converted to an object.
|
||||
* </p>
|
||||
*/
|
||||
protected static class StaticArg implements Comparator<StaticArg>
|
||||
{
|
||||
public final String name;
|
||||
public final String value;
|
||||
|
||||
public StaticArg(String name, String value)
|
||||
{
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(StaticArg o1, StaticArg o2)
|
||||
{
|
||||
return o1.name.compareTo(o2.name);
|
||||
}
|
||||
}
|
||||
|
||||
private final AvailableEncoders encoders;
|
||||
private final AvailableDecoders decoders;
|
||||
private final EndpointConfig endpointConfig;
|
||||
private List<StaticArg> staticArgs;
|
||||
|
||||
public JsrEndpointFunctions(Object endpoint, WebSocketPolicy policy, Executor executor,
|
||||
AvailableEncoders encoders, AvailableDecoders decoders,
|
||||
Map<String, String> uriParams, EndpointConfig endpointConfig)
|
||||
{
|
||||
super(endpoint, policy, executor);
|
||||
this.encoders = encoders;
|
||||
this.decoders = decoders;
|
||||
this.endpointConfig = endpointConfig;
|
||||
|
||||
if (uriParams != null)
|
||||
{
|
||||
this.staticArgs = new ArrayList<>();
|
||||
this.staticArgs.addAll(uriParams.entrySet().stream()
|
||||
.map(entry -> new StaticArg(entry.getKey(), entry.getValue()))
|
||||
.sorted()
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void discoverEndpointFunctions(Object endpoint)
|
||||
{
|
||||
if (endpoint instanceof Endpoint)
|
||||
{
|
||||
Endpoint jsrEndpoint = (Endpoint) endpoint;
|
||||
setOnOpen((session) -> {
|
||||
jsrEndpoint.onOpen(session, endpointConfig);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onOpen", Session.class, EndpointConfig.class)
|
||||
);
|
||||
setOnClose((close) -> {
|
||||
CloseReason closeReason = new CloseReason(
|
||||
CloseReason.CloseCodes.getCloseCode(close.getStatusCode())
|
||||
, close.getReason());
|
||||
jsrEndpoint.onClose(getSession(), closeReason);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onClose", Session.class, EndpointConfig.class)
|
||||
);
|
||||
setOnError((cause) -> {
|
||||
jsrEndpoint.onError(getSession(), cause);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onError", Session.class, EndpointConfig.class)
|
||||
);
|
||||
|
||||
// If using an Endpoint, there's nothing else left to map at this point.
|
||||
// Eventually, the endpoint should call .addMessageHandler() to declare
|
||||
// the various TEXT / BINARY / PONG message functions
|
||||
return;
|
||||
}
|
||||
|
||||
discoverAnnotatedEndpointFunctions(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic discovery of annotated endpoint functions.
|
||||
*
|
||||
* @param endpoint the endpoint object
|
||||
*/
|
||||
protected void discoverAnnotatedEndpointFunctions(Object endpoint)
|
||||
{
|
||||
Class<?> endpointClass = endpoint.getClass();
|
||||
|
||||
// Use the JSR/Client annotation
|
||||
ClientEndpoint websocket = endpointClass.getAnnotation(ClientEndpoint.class);
|
||||
|
||||
if (websocket != null)
|
||||
{
|
||||
encoders.registerAll(websocket.encoders());
|
||||
decoders.registerAll(websocket.decoders());
|
||||
|
||||
// From here, the discovery of endpoint method is standard across
|
||||
// both JSR356/Client and JSR356/Server endpoints
|
||||
try
|
||||
{
|
||||
discoverJsrAnnotatedEndpointFunctions(endpoint);
|
||||
}
|
||||
catch (DecodeException e)
|
||||
{
|
||||
throw new InvalidWebSocketException("Cannot instantiate WebSocket", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSR356 Specific discovery of Annotated Endpoint Methods
|
||||
*
|
||||
* @param endpoint the endpoint
|
||||
*/
|
||||
protected void discoverJsrAnnotatedEndpointFunctions(Object endpoint) throws DecodeException
|
||||
{
|
||||
Class<?> endpointClass = endpoint.getClass();
|
||||
Method method = null;
|
||||
|
||||
// OnOpen [0..1]
|
||||
method = ReflectUtils.findAnnotatedMethod(endpointClass, OnOpen.class);
|
||||
if (method != null)
|
||||
{
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
ReflectUtils.assertIsReturn(method, Void.TYPE);
|
||||
|
||||
// Analyze @OnOpen method declaration techniques
|
||||
DynamicArgs.Builder builder = createDynamicArgs(
|
||||
new Arg(Session.class),
|
||||
new Arg(EndpointConfig.class));
|
||||
|
||||
DynamicArgs.Signature sig = builder.getMatchingSignature(method);
|
||||
assertSignatureValid(sig, OnOpen.class, method);
|
||||
|
||||
final Object[] args = newCallArgs(sig.getCallArgs());
|
||||
DynamicArgs invoker = builder.build(method, sig);
|
||||
setOnOpen((jsrSession) ->
|
||||
{
|
||||
args[0] = jsrSession;
|
||||
args[1] = endpointConfig;
|
||||
invoker.invoke(endpoint, args);
|
||||
return null;
|
||||
}, method);
|
||||
}
|
||||
|
||||
// OnClose [0..1]
|
||||
method = ReflectUtils.findAnnotatedMethod(endpointClass, OnClose.class);
|
||||
if (method != null)
|
||||
{
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
ReflectUtils.assertIsReturn(method, Void.TYPE);
|
||||
|
||||
// Analyze @OnClose method declaration techniques
|
||||
DynamicArgs.Builder builder = createDynamicArgs(
|
||||
new Arg(Session.class),
|
||||
new Arg(CloseReason.class));
|
||||
|
||||
DynamicArgs.Signature sig = builder.getMatchingSignature(method);
|
||||
assertSignatureValid(sig, OnClose.class, method);
|
||||
|
||||
final Object[] args = newCallArgs(sig.getCallArgs());
|
||||
DynamicArgs invoker = builder.build(method, sig);
|
||||
setOnClose((closeInfo) ->
|
||||
{
|
||||
// Convert Jetty CloseInfo to JSR CloseReason
|
||||
CloseReason.CloseCode closeCode = CloseReason.CloseCodes.getCloseCode(closeInfo.getStatusCode());
|
||||
CloseReason closeReason = new CloseReason(closeCode, closeInfo.getReason());
|
||||
args[0] = getSession();
|
||||
args[1] = closeReason;
|
||||
invoker.invoke(endpoint, args);
|
||||
return null;
|
||||
}, method);
|
||||
}
|
||||
|
||||
// OnError [0..1]
|
||||
method = ReflectUtils.findAnnotatedMethod(endpointClass, OnError.class);
|
||||
if (method != null)
|
||||
{
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
ReflectUtils.assertIsReturn(method, Void.TYPE);
|
||||
|
||||
// Analyze @OnError method declaration techniques
|
||||
DynamicArgs.Builder builder = createDynamicArgs(
|
||||
new Arg(Session.class),
|
||||
new Arg(Throwable.class));
|
||||
|
||||
DynamicArgs.Signature sig = builder.getMatchingSignature(method);
|
||||
assertSignatureValid(sig, OnError.class, method);
|
||||
|
||||
final Object[] args = newCallArgs(sig.getCallArgs());
|
||||
DynamicArgs invoker = builder.build(method, sig);
|
||||
setOnError((cause) ->
|
||||
{
|
||||
args[0] = getSession();
|
||||
args[1] = cause;
|
||||
invoker.invoke(endpoint, args);
|
||||
return null;
|
||||
}, method);
|
||||
}
|
||||
|
||||
// OnMessage [0..3] (TEXT / BINARY / PONG)
|
||||
Method messageMethods[] = ReflectUtils.findAnnotatedMethods(endpointClass, OnMessage.class);
|
||||
if (messageMethods != null && messageMethods.length > 0)
|
||||
{
|
||||
for (Method messageMethod : messageMethods)
|
||||
{
|
||||
// Analyze @OnMessage method declaration
|
||||
|
||||
// Must be a public, non-static method
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
|
||||
// If a return type is declared, it must be capable
|
||||
// of being encoded with an available Encoder
|
||||
Class<?> returnType = messageMethod.getReturnType();
|
||||
Encoder returnEncoder = newEncoderFor(returnType);
|
||||
|
||||
// Try to determine Message type (BINARY / TEXT / PONG) from signature
|
||||
|
||||
// Test for Whole TEXT
|
||||
DynamicArgs.Builder builder = createDynamicArgs(
|
||||
new Arg(Session.class),
|
||||
new Arg(CloseReason.class));
|
||||
|
||||
DynamicArgs.Signature sig = builder.getMatchingSignature(method);
|
||||
if(sig != null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Test for Whole BINARY
|
||||
|
||||
// Test for Partial TEXT
|
||||
|
||||
// Test for Partial BINARY
|
||||
|
||||
// Test for Streaming TEXT
|
||||
|
||||
// Test for Streaming BINARY
|
||||
|
||||
// Test for PONG
|
||||
|
||||
// TODO: super.setOnText()
|
||||
// TODO: super.setOnBinary()
|
||||
// TODO: super.setOnPong()
|
||||
|
||||
/*
|
||||
else
|
||||
{
|
||||
// Not a valid @OnMessage declaration signature
|
||||
throw InvalidSignatureException.build(onmsg, OnMessage.class,
|
||||
OnTextFunction.getDynamicArgsBuilder(),
|
||||
OnByteBufferFunction.getDynamicArgsBuilder(),
|
||||
OnByteArrayFunction.getDynamicArgsBuilder(),
|
||||
OnInputStreamFunction.getDynamicArgsBuilder(),
|
||||
OnReaderFunction.getDynamicArgsBuilder());
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Encoder newEncoderFor(Class<?> type)
|
||||
{
|
||||
if ((type == Void.TYPE) || (type == Void.class))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends Encoder> encoderClass = encoders.getEncoderFor(type);
|
||||
if (encoderClass == null)
|
||||
{
|
||||
throw new InvalidWebSocketException("Unable to find Encoder for type " + type.getName());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Encoder encoder = encoderClass.newInstance();
|
||||
encoder.init(this.endpointConfig);
|
||||
return encoder;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
throw new InvalidWebSocketException("Unable to initialize required Encoder: " + encoderClass.getName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSignatureValid(DynamicArgs.Signature sig, Class<? extends Annotation> annotationClass, Method method)
|
||||
{
|
||||
if (sig != null)
|
||||
return;
|
||||
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append('@').append(annotationClass.getSimpleName());
|
||||
err.append(' ');
|
||||
ReflectUtils.append(err, endpoint.getClass(), method);
|
||||
throw new InvalidSignatureException(err.toString());
|
||||
}
|
||||
|
||||
private Object[] newCallArgs(Arg[] callArgs) throws DecodeException
|
||||
{
|
||||
int len = callArgs.length;
|
||||
Object[] args = new Object[callArgs.length];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Object staticValue = getDecodedStaticValue(callArgs[i].getName(), callArgs[i].getType());
|
||||
if (staticValue != null)
|
||||
{
|
||||
args[i] = staticValue;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private Object getDecodedStaticValue(String name, Class<?> type) throws DecodeException
|
||||
{
|
||||
for (StaticArg args : staticArgs)
|
||||
{
|
||||
if (args.name.equals(name))
|
||||
{
|
||||
return AvailableDecoders.decodePrimitive(args.value, type);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private DynamicArgs.Builder createDynamicArgs(Arg... args)
|
||||
{
|
||||
DynamicArgs.Builder argBuilder = new DynamicArgs.Builder();
|
||||
int argCount = args.length;
|
||||
if (this.staticArgs != null)
|
||||
argCount += this.staticArgs.size();
|
||||
|
||||
Arg callArgs[] = new Arg[argCount];
|
||||
int idx = 0;
|
||||
for (Arg arg : args)
|
||||
{
|
||||
callArgs[idx++] = arg;
|
||||
}
|
||||
|
||||
if (this.staticArgs != null)
|
||||
{
|
||||
for (StaticArg staticArg : this.staticArgs)
|
||||
{
|
||||
// TODO: translate from UriParam String to method param type?
|
||||
// TODO: shouldn't this be the Arg seen in the method?
|
||||
// TODO: use decoder?
|
||||
callArgs[idx++] = new Arg(staticArg.value.getClass()).setTag(staticArg.name);
|
||||
}
|
||||
}
|
||||
|
||||
argBuilder.addSignature(new UnorderedSignature(callArgs));
|
||||
|
||||
return argBuilder;
|
||||
}
|
||||
}
|
|
@ -16,25 +16,24 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.functions;
|
||||
package org.eclipse.jetty.websocket.jsr356.function;
|
||||
|
||||
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.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.UnorderedSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* javax.websocket {@link OnMessage} method {@link Function} for BINARY/byte[] types
|
||||
*/
|
||||
@Deprecated
|
||||
public class JsrOnByteArrayFunction implements Function<byte[], Void>
|
||||
{
|
||||
private static final Arg ARG_SESSION = new Arg(Session.class);
|
||||
|
@ -102,14 +101,7 @@ public class JsrOnByteArrayFunction implements Function<byte[], Void>
|
|||
params[idx++] = bin.length;
|
||||
// TODO: add PathParam Arg Values?
|
||||
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, params);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call binary message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, params);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,9 +16,8 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.functions;
|
||||
package org.eclipse.jetty.websocket.jsr356.function;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Function;
|
||||
|
@ -26,16 +25,16 @@ import java.util.function.Function;
|
|||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.UnorderedSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* javax.websocket {@link OnMessage} method {@link Function} for BINARY/{@link ByteBuffer} types
|
||||
*/
|
||||
@Deprecated
|
||||
public class JsrOnByteBufferFunction implements Function<ByteBuffer, Void>
|
||||
{
|
||||
private static final Arg ARG_SESSION = new Arg(Session.class);
|
||||
|
@ -96,15 +95,7 @@ public class JsrOnByteBufferFunction implements Function<ByteBuffer, Void>
|
|||
params[idx++] = session;
|
||||
params[idx++] = bin;
|
||||
|
||||
// TODO: add PathParam Arg Values?
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, params);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call binary message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, params);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,27 +16,26 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.functions;
|
||||
package org.eclipse.jetty.websocket.jsr356.function;
|
||||
|
||||
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.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.UnorderedSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* javax.websocket {@link OnMessage} method {@link Function} for BINARY/{@link InputStream} streaming
|
||||
* types
|
||||
*/
|
||||
@Deprecated
|
||||
public class JsrOnInputStreamFunction implements Function<InputStream, Void>
|
||||
{
|
||||
private static final Arg ARG_SESSION = new Arg(Session.class);
|
||||
|
@ -98,14 +97,7 @@ public class JsrOnInputStreamFunction implements Function<InputStream, Void>
|
|||
params[idx++] = stream;
|
||||
// TODO: add PathParam Arg Values?
|
||||
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, params);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call binary message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, params);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.functions;
|
||||
package org.eclipse.jetty.websocket.jsr356.function;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -25,7 +25,7 @@ import java.util.function.Function;
|
|||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.functions;
|
||||
package org.eclipse.jetty.websocket.jsr356.function;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
|
@ -1,112 +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.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.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* javax.websocket {@link OnClose} method {@link Function}
|
||||
*/
|
||||
public class JsrOnCloseFunction implements Function<CloseInfo, Void>
|
||||
{
|
||||
private static final Arg ARG_SESSION = new Arg(Session.class);
|
||||
private static final Arg ARG_STATUS_CODE = new Arg(int.class);
|
||||
private static final Arg ARG_REASON = new Arg(String.class);
|
||||
|
||||
private final Arg[] extraArgs;
|
||||
private final int paramCount;
|
||||
private final Session session;
|
||||
private final Object endpoint;
|
||||
private final Method method;
|
||||
private final DynamicArgs callable;
|
||||
|
||||
public JsrOnCloseFunction(Session session, Object endpoint, Method method, Arg[] extraArgs)
|
||||
{
|
||||
this.session = session;
|
||||
this.endpoint = endpoint;
|
||||
this.method = method;
|
||||
this.extraArgs = extraArgs;
|
||||
|
||||
// Validate Method
|
||||
ReflectUtils.assertIsAnnotated(method, OnClose.class);
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
ReflectUtils.assertIsReturn(method, Void.TYPE);
|
||||
|
||||
// Build up dynamic callable
|
||||
DynamicArgs.Builder argBuilder = new DynamicArgs.Builder();
|
||||
int argCount = 3;
|
||||
if (this.extraArgs != null)
|
||||
argCount += extraArgs.length;
|
||||
|
||||
this.paramCount = argCount;
|
||||
|
||||
Arg[] callArgs = new Arg[argCount];
|
||||
int idx = 0;
|
||||
callArgs[idx++] = ARG_SESSION;
|
||||
callArgs[idx++] = ARG_STATUS_CODE;
|
||||
callArgs[idx++] = ARG_REASON;
|
||||
for (Arg arg : this.extraArgs)
|
||||
{
|
||||
callArgs[idx++] = arg;
|
||||
}
|
||||
|
||||
argBuilder.addSignature(new UnorderedSignature(callArgs));
|
||||
|
||||
// Attempt to build callable
|
||||
this.callable = argBuilder.build(method, callArgs);
|
||||
if (this.callable == null)
|
||||
{
|
||||
throw InvalidSignatureException.build(method, OnClose.class, argBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void apply(CloseInfo closeinfo)
|
||||
{
|
||||
Object params[] = new Object[paramCount];
|
||||
int idx = 0;
|
||||
params[idx++] = session;
|
||||
params[idx++] = closeinfo.getStatusCode();
|
||||
params[idx++] = closeinfo.getReason();
|
||||
// TODO: add PathParam Arg Values?
|
||||
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, params);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call close method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,108 +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.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.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* javax.websocket {@link OnError} method {@link Function}
|
||||
*/
|
||||
public class JsrOnErrorFunction implements Function<Throwable, Void>
|
||||
{
|
||||
private static final Arg ARG_SESSION = new Arg(Session.class);
|
||||
private static final Arg ARG_CAUSE = new Arg(Throwable.class);
|
||||
|
||||
private final Arg[] extraArgs;
|
||||
private final int paramCount;
|
||||
private final Session session;
|
||||
private final Object endpoint;
|
||||
private final Method method;
|
||||
private final DynamicArgs callable;
|
||||
|
||||
public JsrOnErrorFunction(Session session, Object endpoint, Method method, Arg[] extraArgs)
|
||||
{
|
||||
this.session = session;
|
||||
this.endpoint = endpoint;
|
||||
this.method = method;
|
||||
this.extraArgs = extraArgs;
|
||||
|
||||
// Validate Method
|
||||
ReflectUtils.assertIsAnnotated(method, OnError.class);
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
ReflectUtils.assertIsReturn(method, Void.TYPE);
|
||||
|
||||
// Build up dynamic callable
|
||||
DynamicArgs.Builder argBuilder = new DynamicArgs.Builder();
|
||||
int argCount = 2;
|
||||
if (this.extraArgs != null)
|
||||
argCount += extraArgs.length;
|
||||
|
||||
this.paramCount = argCount;
|
||||
|
||||
Arg[] callArgs = new Arg[argCount];
|
||||
int idx = 0;
|
||||
callArgs[idx++] = ARG_SESSION;
|
||||
callArgs[idx++] = ARG_CAUSE;
|
||||
for (Arg arg : this.extraArgs)
|
||||
{
|
||||
callArgs[idx++] = arg;
|
||||
}
|
||||
|
||||
argBuilder.addSignature(new UnorderedSignature(callArgs));
|
||||
|
||||
// Attempt to build callable
|
||||
this.callable = argBuilder.build(method);
|
||||
if (this.callable == null)
|
||||
{
|
||||
throw InvalidSignatureException.build(method, OnError.class, argBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void apply(Throwable cause)
|
||||
{
|
||||
Object params[] = new Object[paramCount];
|
||||
int idx = 0;
|
||||
params[idx++] = session;
|
||||
params[idx++] = cause;
|
||||
// TODO: add PathParam Arg Values?
|
||||
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, params);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call error method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -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.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.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.common.util.UnorderedSignature;
|
||||
|
||||
/**
|
||||
* javax.websocket {@link OnOpen} method {@link Function}
|
||||
*/
|
||||
public class JsrOnOpenFunction implements Function<org.eclipse.jetty.websocket.api.Session, Void>
|
||||
{
|
||||
private static final Arg ARG_SESSION = new Arg(Session.class);
|
||||
|
||||
private final Arg[] extraArgs;
|
||||
private final int paramCount;
|
||||
private final Session session;
|
||||
private final Object endpoint;
|
||||
private final Method method;
|
||||
private final DynamicArgs callable;
|
||||
|
||||
public JsrOnOpenFunction(Session session, Object endpoint, Method method, DynamicArgs.Arg[] extraArgs)
|
||||
{
|
||||
this.session = session;
|
||||
this.endpoint = endpoint;
|
||||
this.method = method;
|
||||
this.extraArgs = extraArgs;
|
||||
|
||||
// Validate Method
|
||||
ReflectUtils.assertIsAnnotated(method, OnOpen.class);
|
||||
ReflectUtils.assertIsPublicNonStatic(method);
|
||||
ReflectUtils.assertIsReturn(method, Void.TYPE);
|
||||
|
||||
// Build up dynamic callable
|
||||
DynamicArgs.Builder argBuilder = new DynamicArgs.Builder();
|
||||
int argCount = 1;
|
||||
if (this.extraArgs != null)
|
||||
argCount += extraArgs.length;
|
||||
|
||||
this.paramCount = argCount;
|
||||
|
||||
Arg[] callArgs = new Arg[argCount];
|
||||
int idx = 0;
|
||||
callArgs[idx++] = ARG_SESSION;
|
||||
for (Arg arg : this.extraArgs)
|
||||
{
|
||||
callArgs[idx++] = arg;
|
||||
}
|
||||
|
||||
argBuilder.addSignature(new UnorderedSignature(callArgs));
|
||||
|
||||
// Attempt to build callable
|
||||
this.callable = argBuilder.build(method, callArgs);
|
||||
if (this.callable == null)
|
||||
{
|
||||
throw InvalidSignatureException.build(method, OnOpen.class, argBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void apply(org.eclipse.jetty.websocket.api.Session sess)
|
||||
{
|
||||
Object params[] = new Object[paramCount];
|
||||
int idx = 0;
|
||||
params[idx++] = session;
|
||||
// TODO: add PathParam Arg Values?
|
||||
|
||||
try
|
||||
{
|
||||
method.invoke(endpoint, params);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import org.eclipse.jetty.websocket.jsr356.MessageType;
|
|||
/**
|
||||
* Immutable Metadata for a {@link Encoder}
|
||||
*/
|
||||
@Deprecated
|
||||
public class EncoderMetadata extends CoderMetadata<Encoder>
|
||||
{
|
||||
public EncoderMetadata(Class<? extends Encoder> coderClass, Class<?> objType, MessageType messageType, boolean streamed)
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
|||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||
|
||||
@Deprecated
|
||||
public class EncoderMetadataSet extends CoderMetadataSet<Encoder, EncoderMetadata>
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -167,7 +167,7 @@ public class AnnotatedEndpointConfigTest
|
|||
public void testEncoders() throws Exception
|
||||
{
|
||||
List<Class<? extends Encoder>> encoders = config.getEncoders();
|
||||
Assert.assertThat("Encoders",encoders,notNullValue());
|
||||
Assert.assertThat("AvailableEncoders",encoders,notNullValue());
|
||||
|
||||
Class<?> expectedClass = TimeEncoder.class;
|
||||
boolean hasExpectedEncoder = false;
|
||||
|
@ -179,7 +179,7 @@ public class AnnotatedEndpointConfigTest
|
|||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue("Client Encoders has " + expectedClass.getName(),hasExpectedEncoder);
|
||||
Assert.assertTrue("Client AvailableEncoders has " + expectedClass.getName(),hasExpectedEncoder);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -38,7 +38,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests against the Encoders class
|
||||
* Tests against the AvailableEncoders class
|
||||
*/
|
||||
public class EncoderFactoryTest
|
||||
{
|
||||
|
|
|
@ -1,132 +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;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.OnOpen;
|
||||
|
||||
import org.eclipse.jetty.websocket.jsr356.EndpointFunctions.Role;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidCloseIntSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidErrorErrorSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidErrorExceptionSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidErrorIntSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidOpenCloseReasonSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidOpenIntSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidOpenSessionIntSocket;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class EndpointFunctions_BadSignaturesTest
|
||||
{
|
||||
public static class Case
|
||||
{
|
||||
public static Case add(List<Case[]> data, Class<?> pojo, Class<? extends Annotation> methodAnnotation, Role role)
|
||||
{
|
||||
Case test = new Case(pojo, methodAnnotation, role);
|
||||
data.add(new Case[]{test});
|
||||
return test;
|
||||
}
|
||||
|
||||
// The websocket pojo to test against
|
||||
final Class<?> pojo;
|
||||
final Class<? extends Annotation> methodAnnotation;
|
||||
final Role role;
|
||||
|
||||
public Case(Class<?> pojo, Class<? extends Annotation> methodAnnotation, Role role)
|
||||
{
|
||||
this.pojo = pojo;
|
||||
this.methodAnnotation = methodAnnotation;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s @%s", pojo.getSimpleName(), methodAnnotation.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<Case[]> data() throws Exception
|
||||
{
|
||||
List<Case[]> data = new ArrayList<>();
|
||||
|
||||
Case.add(data, InvalidCloseIntSocket.class, OnClose.class, Role.CLOSE);
|
||||
Case.add(data, InvalidErrorErrorSocket.class, OnError.class, Role.ERROR);
|
||||
Case.add(data, InvalidErrorExceptionSocket.class, OnError.class, Role.ERROR);
|
||||
Case.add(data, InvalidErrorIntSocket.class, OnError.class, Role.ERROR);
|
||||
Case.add(data, InvalidOpenCloseReasonSocket.class, OnOpen.class, Role.OPEN);
|
||||
Case.add(data, InvalidOpenIntSocket.class, OnOpen.class, Role.OPEN);
|
||||
Case.add(data, InvalidOpenSessionIntSocket.class, OnOpen.class, Role.OPEN);
|
||||
|
||||
// TODO: invalid return types
|
||||
// TODO: static methods
|
||||
// TODO: private or protected methods
|
||||
// TODO: abstract methods
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Parameterized.Parameter(0)
|
||||
public Case testcase;
|
||||
|
||||
@Test
|
||||
public void testInvalidSignature()
|
||||
{
|
||||
EndpointFunctions functions = new EndpointFunctions();
|
||||
|
||||
Method foundMethod = null;
|
||||
|
||||
for (Method method : testcase.pojo.getDeclaredMethods())
|
||||
{
|
||||
if (method.getAnnotation(testcase.methodAnnotation) != null)
|
||||
{
|
||||
foundMethod = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertThat("Found Method with @" + testcase.methodAnnotation.getSimpleName(), foundMethod, notNullValue());
|
||||
|
||||
try
|
||||
{
|
||||
EndpointFunctions.ArgRole argRole = functions.getArgRole(foundMethod, testcase.methodAnnotation, testcase.role);
|
||||
fail("Expected " + InvalidSignatureException.class + " with message that references " + testcase.methodAnnotation.getSimpleName() + " annotation");
|
||||
}
|
||||
catch (InvalidSignatureException e)
|
||||
{
|
||||
assertThat("Message", e.getMessage(), containsString(testcase.methodAnnotation.getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,204 +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;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.PongMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.jsr356.EndpointFunctions.Role;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicBinaryMessageByteBufferSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicErrorSessionSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicErrorSessionThrowableSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicErrorSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicErrorThrowableSessionSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicErrorThrowableSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicInputStreamSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicInputStreamWithThrowableSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenSessionSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicPongMessageSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicTextMessageStringSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseReasonSessionSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseReasonSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseSessionReasonSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseSocket;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class EndpointFunctions_GoodSignaturesTest
|
||||
{
|
||||
static class ExpectedMethod
|
||||
{
|
||||
String methodName;
|
||||
Class<?> params[];
|
||||
}
|
||||
|
||||
static class ActualMethod
|
||||
{
|
||||
EndpointFunctions.ArgRole argRole;
|
||||
Method method;
|
||||
}
|
||||
|
||||
public static class Case
|
||||
{
|
||||
public static Case add(List<Case[]> data, Class<?> pojo)
|
||||
{
|
||||
Case test = new Case(pojo);
|
||||
data.add(new Case[]{test});
|
||||
return test;
|
||||
}
|
||||
|
||||
// The websocket pojo to test against
|
||||
final Class<?> pojo;
|
||||
// The expected roles found, along with select methods that should
|
||||
// have been identified
|
||||
Map<Role, ExpectedMethod> expectedRoles;
|
||||
|
||||
public Case(Class<?> pojo)
|
||||
{
|
||||
this.pojo = pojo;
|
||||
this.expectedRoles = new HashMap<>();
|
||||
}
|
||||
|
||||
public void addExpected(Role role, String methodName, Class<?>... params)
|
||||
{
|
||||
ExpectedMethod expected = new ExpectedMethod();
|
||||
expected.methodName = methodName;
|
||||
expected.params = params;
|
||||
expectedRoles.put(role, expected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s [%d roles]", pojo.getSimpleName(), expectedRoles.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<Case[]> data() throws Exception
|
||||
{
|
||||
List<Case[]> data = new ArrayList<>();
|
||||
|
||||
Case.add(data, BasicOpenSocket.class)
|
||||
.addExpected(Role.OPEN, "onOpen");
|
||||
Case.add(data, BasicOpenSessionSocket.class)
|
||||
.addExpected(Role.OPEN, "onOpen", Session.class);
|
||||
|
||||
Case.add(data, CloseSocket.class)
|
||||
.addExpected(Role.CLOSE, "onClose");
|
||||
Case.add(data, CloseReasonSocket.class)
|
||||
.addExpected(Role.CLOSE, "onClose", CloseReason.class);
|
||||
Case.add(data, CloseReasonSessionSocket.class)
|
||||
.addExpected(Role.CLOSE, "onClose", CloseReason.class, Session.class);
|
||||
Case.add(data, CloseSessionReasonSocket.class)
|
||||
.addExpected(Role.CLOSE, "onClose", Session.class, CloseReason.class);
|
||||
|
||||
Case.add(data, BasicErrorSocket.class)
|
||||
.addExpected(Role.ERROR, "onError");
|
||||
Case.add(data, BasicErrorSessionSocket.class)
|
||||
.addExpected(Role.ERROR, "onError", Session.class);
|
||||
Case.add(data, BasicErrorSessionThrowableSocket.class)
|
||||
.addExpected(Role.ERROR, "onError", Session.class, Throwable.class);
|
||||
Case.add(data, BasicErrorThrowableSocket.class)
|
||||
.addExpected(Role.ERROR, "onError", Throwable.class);
|
||||
Case.add(data, BasicErrorThrowableSessionSocket.class)
|
||||
.addExpected(Role.ERROR, "onError", Throwable.class, Session.class);
|
||||
|
||||
Case.add(data, BasicTextMessageStringSocket.class)
|
||||
.addExpected(Role.TEXT, "onText", String.class);
|
||||
|
||||
Case.add(data, BasicBinaryMessageByteBufferSocket.class)
|
||||
.addExpected(Role.BINARY, "onBinary", ByteBuffer.class);
|
||||
|
||||
Case.add(data, BasicPongMessageSocket.class)
|
||||
.addExpected(Role.PONG, "onPong", PongMessage.class);
|
||||
|
||||
Case.add(data, BasicInputStreamSocket.class)
|
||||
.addExpected(Role.BINARY_STREAM, "onBinary", InputStream.class);
|
||||
Case.add(data, BasicInputStreamWithThrowableSocket.class)
|
||||
.addExpected(Role.BINARY_STREAM, "onBinary", InputStream.class);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Parameterized.Parameter(0)
|
||||
public Case testcase;
|
||||
|
||||
@Test
|
||||
public void testFoundRoles()
|
||||
{
|
||||
EndpointFunctions functions = new EndpointFunctions();
|
||||
|
||||
// Walk all methods and see what is found
|
||||
Map<Role, ActualMethod> actualMap = new HashMap<>();
|
||||
|
||||
for (Method method : testcase.pojo.getDeclaredMethods())
|
||||
{
|
||||
EndpointFunctions.ArgRole argRole = functions.findArgRole(method);
|
||||
if (argRole != null)
|
||||
{
|
||||
ActualMethod actualMethod = new ActualMethod();
|
||||
actualMethod.argRole = argRole;
|
||||
actualMethod.method = method;
|
||||
|
||||
actualMap.put(argRole.role, actualMethod);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that actual matches found
|
||||
for (Map.Entry<Role, ExpectedMethod> expected : testcase.expectedRoles.entrySet())
|
||||
{
|
||||
// Expected
|
||||
Role expectedRole = expected.getKey();
|
||||
ExpectedMethod expectedMethod = expected.getValue();
|
||||
|
||||
// Actual
|
||||
ActualMethod actual = actualMap.get(expectedRole);
|
||||
assertThat("Role", actual.argRole.role, is(expectedRole));
|
||||
assertThat("Method.name", actual.method.getName(), is(expectedMethod.methodName));
|
||||
|
||||
// validate parameters
|
||||
Class<?> actualParams[] = actual.method.getParameterTypes();
|
||||
Class<?> expectedParams[] = expectedMethod.params;
|
||||
|
||||
assertThat("param count", actualParams.length, is(expectedParams.length));
|
||||
for (int i = 0; i < actualParams.length; i++)
|
||||
{
|
||||
assertThat("Param[" + i + "]", actualParams[i], equalTo(expectedParams[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.decoders;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Instant;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Decoder;
|
||||
import javax.websocket.EndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.util.Hex;
|
||||
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AvailableDecodersTest
|
||||
{
|
||||
private static EndpointConfig testConfig;
|
||||
|
||||
@BeforeClass
|
||||
public static void initConfig()
|
||||
{
|
||||
testConfig = new EmptyClientEndpointConfig();
|
||||
}
|
||||
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
|
||||
private <T> void assertTextDecoder(Class<T> type, String value, T expectedDecoded) throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Class<? extends Decoder> decoderClass = decoders.getDecoderFor(type);
|
||||
assertThat("Decoder Class", decoderClass, notNullValue());
|
||||
|
||||
Decoder.Text<T> decoder = (Decoder.Text<T>) decoderClass.newInstance();
|
||||
decoder.init(testConfig);
|
||||
T decoded = decoder.decode(value);
|
||||
|
||||
assertThat("Decoded", decoded, is(expectedDecoded));
|
||||
}
|
||||
|
||||
private <T> void assertBinaryDecoder(Class<T> type, ByteBuffer value, T expectedDecoded) throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Class<? extends Decoder> decoderClass = decoders.getDecoderFor(type);
|
||||
assertThat("Decoder Class", decoderClass, notNullValue());
|
||||
|
||||
Decoder.Binary<T> decoder = (Decoder.Binary<T>) decoderClass.newInstance();
|
||||
decoder.init(testConfig);
|
||||
T decoded = decoder.decode(value);
|
||||
|
||||
assertThat("Decoded", decoded, equalTo(expectedDecoded));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Boolean() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Boolean expected = Boolean.TRUE;
|
||||
assertTextDecoder(Boolean.class, "true", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_boolean() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
boolean expected = false;
|
||||
assertTextDecoder(Boolean.TYPE, "false", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Byte() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Byte expected = new Byte((byte) 0x21);
|
||||
assertTextDecoder(Byte.class, "33", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_byte() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
byte expected = 0x21;
|
||||
assertTextDecoder(Byte.TYPE, "33", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Character() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Character expected = new Character('!');
|
||||
assertTextDecoder(Character.class, "!", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_char() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
char expected = '!';
|
||||
assertTextDecoder(Character.TYPE, "!", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Double() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Double expected = new Double(123.45);
|
||||
assertTextDecoder(Double.class, "123.45", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_double() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
double expected = 123.45;
|
||||
assertTextDecoder(Double.TYPE, "123.45", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Float() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Float expected = new Float(123.4567);
|
||||
assertTextDecoder(Float.class, "123.4567", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_float() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
float expected = 123.4567F;
|
||||
assertTextDecoder(Float.TYPE, "123.4567", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Integer() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Integer expected = new Integer(1234);
|
||||
assertTextDecoder(Integer.class, "1234", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_int() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
int expected = 1234;
|
||||
assertTextDecoder(Integer.TYPE, "1234", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_Long() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
Long expected = new Long(123_456_789);
|
||||
assertTextDecoder(Long.class, "123456789", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_long() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
long expected = 123_456_789L;
|
||||
assertTextDecoder(Long.TYPE, "123456789", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_String() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
String expected = "Hello World";
|
||||
assertTextDecoder(String.class, "Hello World", expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_ByteBuffer() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
ByteBuffer val = Hex.asByteBuffer("112233445566778899");
|
||||
ByteBuffer expected = Hex.asByteBuffer("112233445566778899");
|
||||
assertBinaryDecoder(ByteBuffer.class, val, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreDecode_ByteArray() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
ByteBuffer val = Hex.asByteBuffer("112233445566778899");
|
||||
byte expected[] = Hex.asByteArray("112233445566778899");
|
||||
assertBinaryDecoder(byte[].class, val, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomDecoder_Time() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
decoders.register(TimeDecoder.class);
|
||||
|
||||
String val = "12:34:56 GMT";
|
||||
|
||||
Date epoch = Date.from(Instant.EPOCH);
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
calendar.setTime(epoch);
|
||||
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 12);
|
||||
calendar.set(Calendar.MINUTE, 34);
|
||||
calendar.set(Calendar.SECOND, 56);
|
||||
|
||||
Date expected = calendar.getTime();
|
||||
assertTextDecoder(Date.class, val, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomDecoder_Date() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
decoders.register(DateDecoder.class);
|
||||
|
||||
String val = "2016.08.22";
|
||||
|
||||
Date epoch = Date.from(Instant.EPOCH);
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
calendar.setTime(epoch);
|
||||
|
||||
calendar.set(Calendar.YEAR, 2016);
|
||||
calendar.set(Calendar.MONTH, Calendar.AUGUST);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 22);
|
||||
|
||||
Date expected = calendar.getTime();
|
||||
assertTextDecoder(Date.class, val, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomDecoder_DateTime() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
decoders.register(DateTimeDecoder.class);
|
||||
|
||||
String val = "2016.08.22 AD at 12:34:56 GMT";
|
||||
|
||||
Date epoch = Date.from(Instant.EPOCH);
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
calendar.setTime(epoch);
|
||||
|
||||
calendar.set(Calendar.YEAR, 2016);
|
||||
calendar.set(Calendar.MONTH, Calendar.AUGUST);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 22);
|
||||
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 12);
|
||||
calendar.set(Calendar.MINUTE, 34);
|
||||
calendar.set(Calendar.SECOND, 56);
|
||||
|
||||
Date expected = calendar.getTime();
|
||||
assertTextDecoder(Date.class, val, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomDecoder_ValidDual_Text() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
decoders.register(ValidDualDecoder.class);
|
||||
|
||||
String val = "[1,234,567]";
|
||||
Integer expected = 1234567;
|
||||
|
||||
assertTextDecoder(Integer.class, val, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomDecoder_ValidDual_Binary() throws IllegalAccessException, InstantiationException, DecodeException
|
||||
{
|
||||
decoders.register(ValidDualDecoder.class);
|
||||
|
||||
ByteBuffer val = ByteBuffer.allocate(16);
|
||||
val.put((byte) '[');
|
||||
val.putLong(0x112233445566L);
|
||||
val.put((byte) ']');
|
||||
val.flip();
|
||||
Long expected = 0x112233445566L;
|
||||
|
||||
assertBinaryDecoder(Long.class, val, expected);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.decoders;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Decoder;
|
||||
|
@ -31,12 +32,16 @@ import javax.websocket.EndpointConfig;
|
|||
*/
|
||||
public class DateDecoder implements Decoder.Text<Date>
|
||||
{
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
@Override
|
||||
public Date decode(String s) throws DecodeException
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SimpleDateFormat("yyyy.MM.dd").parse(s);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd");
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.parse(s);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.decoders;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Decoder;
|
||||
|
@ -31,16 +32,20 @@ import javax.websocket.EndpointConfig;
|
|||
*/
|
||||
public class DateTimeDecoder implements Decoder.Text<Date>
|
||||
{
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
@Override
|
||||
public Date decode(String s) throws DecodeException
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z").parse(s);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.parse(s);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new DecodeException(s,e.getMessage(),e);
|
||||
throw new DecodeException(s, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.decoders;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Decoder;
|
||||
|
@ -31,16 +32,20 @@ import javax.websocket.EndpointConfig;
|
|||
*/
|
||||
public class TimeDecoder implements Decoder.Text<Date>
|
||||
{
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
@Override
|
||||
public Date decode(String s) throws DecodeException
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SimpleDateFormat("HH:mm:ss z").parse(s);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss z");
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.parse(s);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new DecodeException(s,e.getMessage(),e);
|
||||
throw new DecodeException(s, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
package org.eclipse.jetty.websocket.jsr356.decoders;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.websocket.DecodeException;
|
||||
import javax.websocket.Decoder;
|
||||
|
@ -32,13 +34,42 @@ public class ValidDualDecoder implements Decoder.Text<Integer>, Decoder.Binary<L
|
|||
@Override
|
||||
public Long decode(ByteBuffer bytes) throws DecodeException
|
||||
{
|
||||
return bytes.getLong();
|
||||
if (bytes.get() != '[')
|
||||
throw new DecodeException(bytes, "Unexpected opening byte");
|
||||
long val = bytes.getLong();
|
||||
if (bytes.get() != ']')
|
||||
throw new DecodeException(bytes, "Unexpected closing byte");
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer decode(String s) throws DecodeException
|
||||
{
|
||||
return Integer.parseInt(s);
|
||||
DecimalFormat numberFormat = new DecimalFormat("[#,###]");
|
||||
try
|
||||
{
|
||||
Number number = numberFormat.parse(s);
|
||||
if (number instanceof Long)
|
||||
{
|
||||
Long val = (Long) number;
|
||||
if (val > Integer.MAX_VALUE)
|
||||
{
|
||||
throw new DecodeException(s, "Value exceeds Integer.MAX_VALUE");
|
||||
}
|
||||
return val.intValue();
|
||||
}
|
||||
|
||||
if (number instanceof Integer)
|
||||
{
|
||||
return (Integer) number;
|
||||
}
|
||||
|
||||
throw new DecodeException(s, "Unrecognized number format");
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new DecodeException(s, "Unable to parse number", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.encoders;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
import javax.websocket.EndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.util.Hex;
|
||||
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AvailableEncodersTest
|
||||
{
|
||||
private static EndpointConfig testConfig;
|
||||
|
||||
@BeforeClass
|
||||
public static void initConfig()
|
||||
{
|
||||
testConfig = new EmptyClientEndpointConfig();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
|
||||
public <T> void assertTextEncoder(Class<T> type, T value, String expectedEncoded) throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
Class<? extends Encoder> encoderClass = encoders.getEncoderFor(type);
|
||||
assertThat("Encoder Class", encoderClass, notNullValue());
|
||||
|
||||
Encoder.Text<T> encoder = (Encoder.Text<T>) encoderClass.newInstance();
|
||||
encoder.init(testConfig);
|
||||
String encoded = encoder.encode(value);
|
||||
|
||||
assertThat("Encoded", encoded, is(expectedEncoded));
|
||||
}
|
||||
|
||||
public <T> void assertTextStreamEncoder(Class<T> type, T value, String expectedEncoded) throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
Class<? extends Encoder> encoderClass = encoders.getEncoderFor(type);
|
||||
assertThat("Encoder Class", encoderClass, notNullValue());
|
||||
|
||||
Encoder.TextStream<T> encoder = (Encoder.TextStream<T>) encoderClass.newInstance();
|
||||
encoder.init(testConfig);
|
||||
StringWriter writer = new StringWriter();
|
||||
encoder.encode(value, writer);
|
||||
|
||||
assertThat("Encoded", writer.toString(), is(expectedEncoded));
|
||||
}
|
||||
|
||||
public <T> void assertBinaryEncoder(Class<T> type, T value, String expectedEncodedHex) throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
AvailableEncoders encoders = new AvailableEncoders();
|
||||
Class<? extends Encoder> encoderClass = encoders.getEncoderFor(type);
|
||||
assertThat("Encoder Class", encoderClass, notNullValue());
|
||||
|
||||
Encoder.Binary<T> encoder = (Encoder.Binary<T>) encoderClass.newInstance();
|
||||
encoder.init(testConfig);
|
||||
ByteBuffer encoded = encoder.encode(value);
|
||||
|
||||
String hexEncoded = Hex.asHex(encoded);
|
||||
assertThat("Encoded", hexEncoded, is(expectedEncodedHex));
|
||||
}
|
||||
|
||||
public <T> void assertBinaryStreamEncoder(Class<T> type, T value, String expectedEncodedHex) throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
Class<? extends Encoder> encoderClass = encoders.getEncoderFor(type);
|
||||
assertThat("Encoder Class", encoderClass, notNullValue());
|
||||
|
||||
Encoder.BinaryStream<T> encoder = (Encoder.BinaryStream<T>) encoderClass.newInstance();
|
||||
encoder.init(testConfig);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
encoder.encode(value, out);
|
||||
|
||||
String hexEncoded = Hex.asHex(out.toByteArray());
|
||||
|
||||
assertThat("Encoded", hexEncoded, is(expectedEncodedHex));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Boolean() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Boolean.class, Boolean.TRUE, "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_bool() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Boolean.TYPE, true, "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Byte() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Byte.class, new Byte((byte) 0x21), "33");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_byte() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Byte.TYPE, (byte) 0x21, "33");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Character() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Character.class, new Character('!'), "!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_char() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Character.TYPE, '!', "!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Double() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Double.class, new Double(123.45), "123.45");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_double() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
//noinspection RedundantCast
|
||||
assertTextEncoder(Double.TYPE, (double) 123.45, "123.45");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Float() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Float.class, new Float(123.4567), "123.4567");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_float() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
//noinspection RedundantCast
|
||||
assertTextEncoder(Float.TYPE, (float) 123.4567, "123.4567");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Integer() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Integer.class, new Integer(123), "123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_int() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Integer.TYPE, 123, "123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_Long() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Long.class, new Long(123_456_789), "123456789");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_long() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(Long.TYPE, 123_456_789L, "123456789");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_String() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
assertTextEncoder(String.class, "Hello World", "Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_ByteBuffer() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
ByteBuffer buf = Hex.asByteBuffer("1122334455");
|
||||
assertBinaryEncoder(ByteBuffer.class, buf, "1122334455");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoreEncoder_ByteArray() throws IllegalAccessException, InstantiationException, EncodeException
|
||||
{
|
||||
byte buf[] = Hex.asByteArray("998877665544332211");
|
||||
assertBinaryEncoder(byte[].class, buf, "998877665544332211");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEncoder_Time() throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
encoders.register(TimeEncoder.class);
|
||||
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 12);
|
||||
calendar.set(Calendar.MINUTE, 34);
|
||||
calendar.set(Calendar.SECOND, 56);
|
||||
|
||||
Date val = calendar.getTime();
|
||||
assertTextEncoder(Date.class, val, "12:34:56 GMT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEncoder_Date() throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
encoders.register(DateEncoder.class);
|
||||
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
calendar.set(Calendar.YEAR, 2016);
|
||||
calendar.set(Calendar.MONTH, Calendar.AUGUST);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 22);
|
||||
|
||||
Date val = calendar.getTime();
|
||||
assertTextEncoder(Date.class, val, "2016.08.22");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEncoder_DateTime() throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
encoders.register(DateTimeEncoder.class);
|
||||
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
calendar.set(Calendar.YEAR, 2016);
|
||||
calendar.set(Calendar.MONTH, Calendar.AUGUST);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 22);
|
||||
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 12);
|
||||
calendar.set(Calendar.MINUTE, 34);
|
||||
calendar.set(Calendar.SECOND, 56);
|
||||
|
||||
Date val = calendar.getTime();
|
||||
assertTextEncoder(Date.class, val, "2016.08.22 AD at 12:34:56 GMT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEncoder_ValidDual_Text() throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
encoders.register(ValidDualEncoder.class);
|
||||
assertTextEncoder(Integer.class, 1234567, "[1,234,567]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEncoder_ValidDual_Binary() throws IllegalAccessException, InstantiationException, EncodeException, IOException
|
||||
{
|
||||
encoders.register(ValidDualEncoder.class);
|
||||
long value = 0x112233445566L;
|
||||
assertBinaryStreamEncoder(Long.class, value, "5B00001122334455665D");
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.encoders;
|
|||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
@ -30,6 +31,8 @@ import javax.websocket.EndpointConfig;
|
|||
*/
|
||||
public class DateEncoder implements Encoder.Text<Date>
|
||||
{
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
|
@ -38,7 +41,9 @@ public class DateEncoder implements Encoder.Text<Date>
|
|||
@Override
|
||||
public String encode(Date object) throws EncodeException
|
||||
{
|
||||
return new SimpleDateFormat("yyyy.MM.dd").format(object);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd");
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.format(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.encoders;
|
|||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
@ -30,6 +31,8 @@ import javax.websocket.EndpointConfig;
|
|||
*/
|
||||
public class DateTimeEncoder implements Encoder.Text<Date>
|
||||
{
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
|
@ -38,7 +41,9 @@ public class DateTimeEncoder implements Encoder.Text<Date>
|
|||
@Override
|
||||
public String encode(Date object) throws EncodeException
|
||||
{
|
||||
return new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z").format(object);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.format(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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.jsr356.encoders;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
import javax.websocket.EndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
|
||||
|
||||
/**
|
||||
* Intentionally bad example of attempting to decode the same object to different message formats.
|
||||
*/
|
||||
public class DualEncoder implements Encoder.Text<Fruit>, Encoder.TextStream<Fruit>
|
||||
{
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(Fruit fruit) throws EncodeException
|
||||
{
|
||||
return String.format("%s|%s",fruit.name,fruit.color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Fruit fruit, Writer writer) throws EncodeException, IOException
|
||||
{
|
||||
writer.write(fruit.name);
|
||||
writer.write('|');
|
||||
writer.write(fruit.color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(EndpointConfig config)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.encoders;
|
|||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
@ -30,6 +31,8 @@ import javax.websocket.EndpointConfig;
|
|||
*/
|
||||
public class TimeEncoder implements Encoder.Text<Date>
|
||||
{
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
|
@ -38,7 +41,9 @@ public class TimeEncoder implements Encoder.Text<Date>
|
|||
@Override
|
||||
public String encode(Date object) throws EncodeException
|
||||
{
|
||||
return new SimpleDateFormat("HH:mm:ss z").format(object);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss z");
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.format(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.jsr356.encoders;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
@ -38,23 +39,17 @@ public class ValidDualEncoder implements Encoder.Text<Integer>, Encoder.BinarySt
|
|||
@Override
|
||||
public String encode(Integer object) throws EncodeException
|
||||
{
|
||||
return Integer.toString(object);
|
||||
return String.format("[%,d]", object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Long object, OutputStream os) throws EncodeException, IOException
|
||||
{
|
||||
byte b[] = new byte[8];
|
||||
long v = object;
|
||||
b[0] = (byte)(v >>> 56);
|
||||
b[1] = (byte)(v >>> 48);
|
||||
b[2] = (byte)(v >>> 40);
|
||||
b[3] = (byte)(v >>> 32);
|
||||
b[4] = (byte)(v >>> 24);
|
||||
b[5] = (byte)(v >>> 16);
|
||||
b[6] = (byte)(v >>> 8);
|
||||
b[7] = (byte)(v >>> 0);
|
||||
os.write(b,0,8);
|
||||
DataOutputStream data = new DataOutputStream(os);
|
||||
data.writeByte((byte) '[');
|
||||
data.writeLong(object);
|
||||
data.writeByte((byte) ']');
|
||||
data.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
|
||||
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
public class JsrEndpointFunctions_BadSignaturesTest
|
||||
{
|
||||
private static ClientContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void initContainer()
|
||||
{
|
||||
container = new ClientContainer();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
private Map<String, String> uriParams = new HashMap<>();
|
||||
private EndpointConfig endpointConfig = new EmptyClientEndpointConfig();
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private void assertBadSocket(TrackingSocket socket, String expectedString) throws Exception
|
||||
{
|
||||
JsrEndpointFunctions functions = new JsrEndpointFunctions(
|
||||
socket,
|
||||
container.getPolicy(),
|
||||
container.getExecutor(),
|
||||
encoders,
|
||||
decoders,
|
||||
uriParams,
|
||||
endpointConfig
|
||||
);
|
||||
|
||||
expectedException.expect(InvalidSignatureException.class);
|
||||
expectedException.expectMessage(containsString(expectedString));
|
||||
functions.start();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public class InvalidOpenCloseReasonSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Open Method Declaration (parameter type CloseReason)
|
||||
* @param reason the close reason
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(CloseReason reason)
|
||||
{
|
||||
/* no impl */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidOpenCloseReasonSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidOpenCloseReasonSocket(), "onOpen");
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public static class InvalidOpenIntSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Open Method Declaration (parameter type int)
|
||||
* @param value the open value
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(int value)
|
||||
{
|
||||
/* no impl */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidOpenIntSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidOpenIntSocket(), "onOpen");
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public static class InvalidOpenSessionIntSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Open Method Declaration (parameter of type int)
|
||||
* @param session the session for the open
|
||||
* @param count the open count
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(Session session, int count)
|
||||
{
|
||||
/* no impl */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidOpenSessionIntSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidOpenSessionIntSocket(), "onOpen");
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public static class InvalidCloseIntSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Close Method Declaration (parameter type int)
|
||||
*
|
||||
* @param statusCode the status code
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(int statusCode)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidCloseIntSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidCloseIntSocket(), "onClose");
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public static class InvalidErrorErrorSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Error Method Declaration (parameter type Error)
|
||||
*
|
||||
* @param error the error
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Error error)
|
||||
{
|
||||
/* no impl */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidErrorErrorSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidErrorErrorSocket(), "onError");
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public static class InvalidErrorExceptionSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Error Method Declaration (parameter type Exception)
|
||||
*
|
||||
* @param e the extension
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Exception e)
|
||||
{
|
||||
/* no impl */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidErrorExceptionSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidErrorExceptionSocket(), "onError");
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
@ClientEndpoint
|
||||
public static class InvalidErrorIntSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid Error Method Declaration (parameter type int)
|
||||
*
|
||||
* @param errorCount the error count
|
||||
*/
|
||||
@OnError
|
||||
public void onError(int errorCount)
|
||||
{
|
||||
/* no impl */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidErrorIntSocket() throws Exception
|
||||
{
|
||||
assertBadSocket(new InvalidErrorIntSocket(), "onError");
|
||||
}
|
||||
|
||||
// TODO: invalid return types
|
||||
// TODO: static methods
|
||||
// TODO: private or protected methods
|
||||
// TODO: abstract methods
|
||||
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
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.client.EmptyClientEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsrEndpointFunctions_OnCloseTest
|
||||
{
|
||||
private static final String EXPECTED_REASON = "CloseReason[1000,Normal]";
|
||||
private static ClientContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void initContainer()
|
||||
{
|
||||
container = new ClientContainer();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
private Map<String,String> uriParams = new HashMap<>();
|
||||
private EndpointConfig endpointConfig = new EmptyClientEndpointConfig();
|
||||
|
||||
public JsrSession newSession(Object websocket)
|
||||
{
|
||||
String id = JsrEndpointFunctions_OnCloseTest.class.getSimpleName();
|
||||
URI requestURI = URI.create("ws://localhost/" + id);
|
||||
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
|
||||
DummyConnection connection = new DummyConnection(policy);
|
||||
ClientEndpointConfig config = new EmptyClientEndpointConfig();
|
||||
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket, config);
|
||||
return new JsrSession(container, id, requestURI, ei, connection);
|
||||
}
|
||||
|
||||
private void assertOnCloseInvocation(TrackingSocket socket, String expectedEventFormat, Object... args) throws Exception
|
||||
{
|
||||
JsrEndpointFunctions endpointFunctions = new JsrEndpointFunctions(
|
||||
socket, container.getPolicy(),
|
||||
container.getExecutor(),
|
||||
encoders,
|
||||
decoders,
|
||||
uriParams,
|
||||
endpointConfig
|
||||
);
|
||||
endpointFunctions.start();
|
||||
|
||||
// These invocations are the same for all tests
|
||||
endpointFunctions.onOpen(newSession(socket));
|
||||
CloseInfo closeInfo = new CloseInfo(StatusCode.NORMAL, "Normal");
|
||||
endpointFunctions.onClose(closeInfo);
|
||||
socket.assertEvent(String.format(expectedEventFormat, args));
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class CloseSocket extends TrackingSocket
|
||||
{
|
||||
@OnClose
|
||||
public void OnClose()
|
||||
{
|
||||
addEvent("OnClose()");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeClose() throws Exception
|
||||
{
|
||||
assertOnCloseInvocation(new CloseSocket(), "OnClose()");
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class CloseSessionSocket extends TrackingSocket
|
||||
{
|
||||
@OnClose
|
||||
public void OnClose(Session session)
|
||||
{
|
||||
addEvent("OnClose(%s)", session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeCloseSession() throws Exception
|
||||
{
|
||||
assertOnCloseInvocation(new CloseSessionSocket(),
|
||||
"OnClose(JsrSession[CLIENT,%s,DummyConnection])",
|
||||
CloseSessionSocket.class.getName());
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class CloseReasonSocket extends TrackingSocket
|
||||
{
|
||||
@OnClose
|
||||
public void OnClose(CloseReason reason)
|
||||
{
|
||||
addEvent("OnClose(%s)", reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeCloseReason() throws Exception
|
||||
{
|
||||
assertOnCloseInvocation(new CloseReasonSocket(),
|
||||
"OnClose(%s)", EXPECTED_REASON);
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class CloseSessionReasonSocket extends TrackingSocket
|
||||
{
|
||||
@OnClose
|
||||
public void OnClose(Session session, CloseReason reason)
|
||||
{
|
||||
addEvent("OnClose(%s, %s)", session, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeCloseSessionReason() throws Exception
|
||||
{
|
||||
assertOnCloseInvocation(new CloseSessionReasonSocket(),
|
||||
"OnClose(JsrSession[CLIENT,%s,DummyConnection], %s)",
|
||||
CloseSessionReasonSocket.class.getName(), EXPECTED_REASON);
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class CloseReasonSessionSocket extends TrackingSocket
|
||||
{
|
||||
@OnClose
|
||||
public void OnClose(CloseReason reason, Session session)
|
||||
{
|
||||
addEvent("OnClose(%s, %s)", reason, session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeCloseReasonSession() throws Exception
|
||||
{
|
||||
assertOnCloseInvocation(new CloseReasonSessionSocket(),
|
||||
"OnClose(%s, JsrSession[CLIENT,%s,DummyConnection])",
|
||||
EXPECTED_REASON, CloseReasonSessionSocket.class.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
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.client.EmptyClientEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsrEndpointFunctions_OnErrorTest
|
||||
{
|
||||
private static final String EXPECTED_THROWABLE = "java.lang.RuntimeException: From Testcase";
|
||||
private static ClientContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void initContainer()
|
||||
{
|
||||
container = new ClientContainer();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
private Map<String, String> uriParams = new HashMap<>();
|
||||
private EndpointConfig endpointConfig = new EmptyClientEndpointConfig();
|
||||
|
||||
public JsrSession newSession(Object websocket)
|
||||
{
|
||||
String id = JsrEndpointFunctions_OnErrorTest.class.getSimpleName();
|
||||
URI requestURI = URI.create("ws://localhost/" + id);
|
||||
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
|
||||
DummyConnection connection = new DummyConnection(policy);
|
||||
ClientEndpointConfig config = new EmptyClientEndpointConfig();
|
||||
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket, config);
|
||||
return new JsrSession(container, id, requestURI, ei, connection);
|
||||
}
|
||||
|
||||
private void assertOnErrorInvocation(TrackingSocket socket, String expectedEventFormat, Object... args) throws Exception
|
||||
{
|
||||
JsrEndpointFunctions endpointFunctions = new JsrEndpointFunctions(
|
||||
socket, container.getPolicy(),
|
||||
container.getExecutor(),
|
||||
encoders,
|
||||
decoders,
|
||||
uriParams,
|
||||
endpointConfig
|
||||
);
|
||||
endpointFunctions.start();
|
||||
|
||||
// These invocations are the same for all tests
|
||||
endpointFunctions.onOpen(newSession(socket));
|
||||
endpointFunctions.onError(new RuntimeException("From Testcase"));
|
||||
socket.assertEvent(String.format(expectedEventFormat, args));
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class ErrorSocket extends TrackingSocket
|
||||
{
|
||||
@OnError
|
||||
public void onError()
|
||||
{
|
||||
addEvent("onError()");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeError() throws Exception
|
||||
{
|
||||
assertOnErrorInvocation(new ErrorSocket(), "onError()");
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class ErrorSessionSocket extends TrackingSocket
|
||||
{
|
||||
@OnError
|
||||
public void onError(Session session)
|
||||
{
|
||||
addEvent("onError(%s)", session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeErrorSession() throws Exception
|
||||
{
|
||||
assertOnErrorInvocation(new ErrorSessionSocket(),
|
||||
"onError(JsrSession[CLIENT,%s,DummyConnection])",
|
||||
ErrorSessionSocket.class.getName());
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class ErrorSessionThrowableSocket extends TrackingSocket
|
||||
{
|
||||
@OnError
|
||||
public void onError(Session session, Throwable cause)
|
||||
{
|
||||
addEvent("onError(%s, %s)", session, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeErrorSessionThrowable() throws Exception
|
||||
{
|
||||
assertOnErrorInvocation(new ErrorSessionThrowableSocket(),
|
||||
"onError(JsrSession[CLIENT,%s,DummyConnection], %s)",
|
||||
ErrorSessionThrowableSocket.class.getName(), EXPECTED_THROWABLE);
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class ErrorThrowableSocket extends TrackingSocket
|
||||
{
|
||||
@OnError
|
||||
public void onError(Throwable cause)
|
||||
{
|
||||
addEvent("onError(%s)", cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeErrorThrowable() throws Exception
|
||||
{
|
||||
assertOnErrorInvocation(new ErrorThrowableSocket(),
|
||||
"onError(%s)", EXPECTED_THROWABLE);
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class ErrorThrowableSessionSocket extends TrackingSocket
|
||||
{
|
||||
@OnError
|
||||
public void onError(Throwable cause, Session session)
|
||||
{
|
||||
addEvent("onError(%s, %s)", cause, session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeErrorThrowableSession() throws Exception
|
||||
{
|
||||
assertOnErrorInvocation(new ErrorThrowableSessionSocket(),
|
||||
"onError(%s, JsrSession[CLIENT,%s,DummyConnection])",
|
||||
EXPECTED_THROWABLE,
|
||||
ErrorThrowableSessionSocket.class.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
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.client.EmptyClientEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsrEndpointFunctions_OnMessage_BinaryTest
|
||||
{
|
||||
private static ClientContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void initContainer()
|
||||
{
|
||||
container = new ClientContainer();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
private Map<String, String> uriParams = new HashMap<>();
|
||||
private EndpointConfig endpointConfig = new EmptyClientEndpointConfig();
|
||||
|
||||
private String expectedBuffer;
|
||||
|
||||
public JsrSession newSession(Object websocket)
|
||||
{
|
||||
String id = JsrEndpointFunctions_OnMessage_BinaryTest.class.getSimpleName();
|
||||
URI requestURI = URI.create("ws://localhost/" + id);
|
||||
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
|
||||
DummyConnection connection = new DummyConnection(policy);
|
||||
ClientEndpointConfig config = new EmptyClientEndpointConfig();
|
||||
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket, config);
|
||||
return new JsrSession(container, id, requestURI, ei, connection);
|
||||
}
|
||||
|
||||
private void assertOnMessageInvocation(TrackingSocket socket, String expectedEventFormat, Object... args) throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
JsrEndpointFunctions endpointFunctions = new JsrEndpointFunctions(
|
||||
socket, container.getPolicy(),
|
||||
container.getExecutor(),
|
||||
encoders,
|
||||
decoders,
|
||||
uriParams,
|
||||
endpointConfig
|
||||
);
|
||||
|
||||
// This invocation is the same for all tests
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap("Hello World".getBytes(StandardCharsets.UTF_8));
|
||||
expectedBuffer = BufferUtil.toDetailString(byteBuffer);
|
||||
endpointFunctions.onBinary(byteBuffer, true);
|
||||
socket.assertEvent(String.format(expectedEventFormat, args));
|
||||
}
|
||||
|
||||
public static class MessageSocket extends TrackingSocket
|
||||
{
|
||||
// TODO: Ambiguous declaration
|
||||
@OnMessage
|
||||
public void onMessage()
|
||||
{
|
||||
addEvent("onMessage()");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMessage() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
assertOnMessageInvocation(new MessageSocket(), "onMessage()");
|
||||
}
|
||||
|
||||
public static class MessageTextSocket extends TrackingSocket
|
||||
{
|
||||
@OnMessage
|
||||
public void onMessage(String msg)
|
||||
{
|
||||
addEvent("onMessage(%s)", msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMessageText() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
assertOnMessageInvocation(new MessageTextSocket(), "onMessage(Hello World)");
|
||||
}
|
||||
|
||||
public static class MessageSessionSocket extends TrackingSocket
|
||||
{
|
||||
// TODO: Ambiguous declaration
|
||||
@OnMessage
|
||||
public void onMessage(Session session)
|
||||
{
|
||||
addEvent("onMessage(%s)", session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMessageSession() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
assertOnMessageInvocation(new MessageSessionSocket(),
|
||||
"onMessage(JsrSession[CLIENT,%s,DummyConnection])",
|
||||
MessageSessionSocket.class.getName());
|
||||
}
|
||||
|
||||
public static class MessageSessionTextSocket extends TrackingSocket
|
||||
{
|
||||
@OnMessage
|
||||
public void onMessage(Session session, String msg)
|
||||
{
|
||||
|
||||
addEvent("onMessage(%s, %s)", session, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMessageSessionText() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
assertOnMessageInvocation(new MessageSessionTextSocket(),
|
||||
"onMessage(JsrSession[CLIENT,%s,DummyConnection], Hello World)",
|
||||
MessageSessionTextSocket.class.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
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.client.EmptyClientEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
public class JsrEndpointFunctions_OnMessage_TextTest
|
||||
{
|
||||
private static ClientContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void initContainer()
|
||||
{
|
||||
container = new ClientContainer();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
private Map<String, String> uriParams = new HashMap<>();
|
||||
private EndpointConfig endpointConfig = new EmptyClientEndpointConfig();
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
public JsrSession newSession(Object websocket)
|
||||
{
|
||||
String id = JsrEndpointFunctions_OnMessage_TextTest.class.getSimpleName();
|
||||
URI requestURI = URI.create("ws://localhost/" + id);
|
||||
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
|
||||
DummyConnection connection = new DummyConnection(policy);
|
||||
ClientEndpointConfig config = new EmptyClientEndpointConfig();
|
||||
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket, config);
|
||||
return new JsrSession(container, id, requestURI, ei, connection);
|
||||
}
|
||||
|
||||
private void onText(TrackingSocket socket, String msg)
|
||||
{
|
||||
JsrEndpointFunctions endpointFunctions = new JsrEndpointFunctions(
|
||||
socket, container.getPolicy(),
|
||||
container.getExecutor(),
|
||||
encoders,
|
||||
decoders,
|
||||
uriParams,
|
||||
endpointConfig
|
||||
);
|
||||
|
||||
// This invocation is the same for all tests
|
||||
ByteBuffer payload = BufferUtil.toBuffer(msg, StandardCharsets.UTF_8);
|
||||
endpointFunctions.onText(payload, true);
|
||||
}
|
||||
|
||||
private void assertOnMessageInvocation(TrackingSocket socket, String expectedEventFormat, Object... args) throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
onText(socket, "Hello World");
|
||||
socket.assertEvent(String.format(expectedEventFormat, args));
|
||||
}
|
||||
|
||||
public static class MessageSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid declaration - the type is ambiguous (is it TEXT / BINARY / PONG?)
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage()
|
||||
{
|
||||
addEvent("onMessage()");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmbiguousEmptyMessage() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
MessageSocket socket = new MessageSocket();
|
||||
expectedException.expect(InvalidSignatureException.class);
|
||||
expectedException.expectMessage(containsString("@OnMessage public void onMessage"));
|
||||
onText(socket, "Hello World");
|
||||
}
|
||||
|
||||
public static class MessageTextSocket extends TrackingSocket
|
||||
{
|
||||
@OnMessage
|
||||
public void onMessage(String msg)
|
||||
{
|
||||
addEvent("onMessage(%s)", msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMessageText() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
assertOnMessageInvocation(new MessageTextSocket(), "onMessage(Hello World)");
|
||||
}
|
||||
|
||||
public static class MessageSessionSocket extends TrackingSocket
|
||||
{
|
||||
/**
|
||||
* Invalid declaration - the type is ambiguous (is it TEXT / BINARY / PONG?)
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(Session session)
|
||||
{
|
||||
addEvent("onMessage(%s)", session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmbiguousMessageSession() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
MessageSessionSocket socket = new MessageSessionSocket();
|
||||
|
||||
expectedException.expect(InvalidSignatureException.class);
|
||||
expectedException.expectMessage(containsString("@OnMessage public void onMessage"));
|
||||
onText(socket, "Hello World");
|
||||
}
|
||||
|
||||
public static class MessageSessionTextSocket extends TrackingSocket
|
||||
{
|
||||
@OnMessage
|
||||
public void onMessage(Session session, String msg)
|
||||
{
|
||||
|
||||
addEvent("onMessage(%s, %s)", session, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMessageSessionText() throws InvocationTargetException, IllegalAccessException
|
||||
{
|
||||
assertOnMessageInvocation(new MessageSessionTextSocket(),
|
||||
"onMessage(JsrSession[CLIENT,%s,DummyConnection], Hello World)",
|
||||
MessageSessionTextSocket.class.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
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.client.EmptyClientEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsrEndpointFunctions_OnOpenTest
|
||||
{
|
||||
private static ClientContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void initContainer()
|
||||
{
|
||||
container = new ClientContainer();
|
||||
}
|
||||
|
||||
private AvailableEncoders encoders = new AvailableEncoders();
|
||||
private AvailableDecoders decoders = new AvailableDecoders();
|
||||
private Map<String,String> uriParams = new HashMap<>();
|
||||
private EndpointConfig endpointConfig = new EmptyClientEndpointConfig();
|
||||
|
||||
public JsrSession newSession(Object websocket)
|
||||
{
|
||||
String id = JsrEndpointFunctions_OnOpenTest.class.getSimpleName();
|
||||
URI requestURI = URI.create("ws://localhost/" + id);
|
||||
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
|
||||
DummyConnection connection = new DummyConnection(policy);
|
||||
ClientEndpointConfig config = new EmptyClientEndpointConfig();
|
||||
ConfiguredEndpoint ei = new ConfiguredEndpoint(websocket, config);
|
||||
return new JsrSession(container, id, requestURI, ei, connection);
|
||||
}
|
||||
|
||||
private void assertOnOpenInvocation(TrackingSocket socket, String expectedEventFormat, Object... args) throws Exception
|
||||
{
|
||||
JsrEndpointFunctions endpointFunctions = new JsrEndpointFunctions(
|
||||
socket, container.getPolicy(),
|
||||
container.getExecutor(),
|
||||
encoders,
|
||||
decoders,
|
||||
uriParams,
|
||||
endpointConfig
|
||||
);
|
||||
|
||||
endpointFunctions.start();
|
||||
|
||||
// This invocation is the same for all tests
|
||||
endpointFunctions.onOpen(newSession(socket));
|
||||
socket.assertEvent(String.format(expectedEventFormat, args));
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class OpenSocket extends TrackingSocket
|
||||
{
|
||||
@OnOpen
|
||||
public void onOpen()
|
||||
{
|
||||
addEvent("onOpen()");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeOpen() throws Exception
|
||||
{
|
||||
assertOnOpenInvocation(new OpenSocket(), "onOpen()");
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class OpenSessionSocket extends TrackingSocket
|
||||
{
|
||||
@OnOpen
|
||||
public void onOpen(Session session)
|
||||
{
|
||||
addEvent("onOpen(%s)", session);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeOpenSession() throws Exception
|
||||
{
|
||||
assertOnOpenInvocation(new OpenSessionSocket(),
|
||||
"onOpen(JsrSession[CLIENT,%s,DummyConnection])",
|
||||
OpenSessionSocket.class.getName());
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ public class EncoderMetadataSetTest
|
|||
|
||||
// has duplicated support for the same target Type
|
||||
coders.add(BadDualEncoder.class);
|
||||
Assert.fail("Should have thrown IllegalStateException for attempting to register Encoders with duplicate implementation");
|
||||
Assert.fail("Should have thrown IllegalStateException for attempting to register AvailableEncoders with duplicate implementation");
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ public class EncoderMetadataSetTest
|
|||
{
|
||||
// Add TimeEncoder (which also wants to decode java.util.Date)
|
||||
coders.add(TimeEncoder.class);
|
||||
Assert.fail("Should have thrown IllegalStateException for attempting to register Encoders with duplicate implementation");
|
||||
Assert.fail("Should have thrown IllegalStateException for attempting to register AvailableEncoders with duplicate implementation");
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
|
|
|
@ -71,7 +71,7 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
|
|||
this.decoders = Collections.unmodifiableList(Arrays.asList(anno.decoders()));
|
||||
}
|
||||
|
||||
// Encoders (favor provided config over annotation)
|
||||
// AvailableEncoders (favor provided config over annotation)
|
||||
if (baseConfig != null && baseConfig.getEncoders() != null && baseConfig.getEncoders().size() > 0)
|
||||
{
|
||||
this.encoders = Collections.unmodifiableList(baseConfig.getEncoders());
|
||||
|
|
|
@ -22,12 +22,33 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class InvalidSignatureException extends InvalidWebSocketException
|
||||
{
|
||||
public static InvalidSignatureException build(Method method, Class<? extends Annotation> annoClass, DynamicArgs.Builder ... dynArgsBuilders)
|
||||
public static InvalidSignatureException build(Class<?> pojo, Class<? extends Annotation> methodAnnotationClass, Method method)
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
if (methodAnnotationClass != null)
|
||||
{
|
||||
err.append("@");
|
||||
err.append(methodAnnotationClass.getSimpleName());
|
||||
err.append(' ');
|
||||
}
|
||||
if (pojo != null)
|
||||
{
|
||||
ReflectUtils.append(err, method);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReflectUtils.append(err, pojo, method);
|
||||
}
|
||||
return new InvalidSignatureException(err.toString());
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -53,6 +74,6 @@ public class InvalidSignatureException extends InvalidWebSocketException
|
|||
|
||||
public InvalidSignatureException(String message, Throwable cause)
|
||||
{
|
||||
super(message,cause);
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,12 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.encoders;
|
||||
package org.eclipse.jetty.websocket.common;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
||||
public class DefaultBinaryEncoder extends AbstractEncoder implements Encoder.Binary<ByteBuffer>
|
||||
/**
|
||||
* Interface tag for endpoints managed by other implementations (such as JSR356)
|
||||
*/
|
||||
public interface ManagedEndpoint
|
||||
{
|
||||
@Override
|
||||
public ByteBuffer encode(ByteBuffer message) throws EncodeException
|
||||
{
|
||||
return message;
|
||||
}
|
||||
Object getRawEndpoint();
|
||||
}
|
|
@ -73,15 +73,16 @@ import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
|
|||
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
|
||||
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.function.OnByteArrayFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnByteBufferFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnCloseFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnErrorFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnFrameFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnInputStreamFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnOpenFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnReaderFunction;
|
||||
import org.eclipse.jetty.websocket.common.function.OnTextFunction;
|
||||
import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
|
||||
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;
|
||||
|
@ -94,7 +95,7 @@ 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.DynamicArgsException;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgsException;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
@ManagedObject("A Jetty WebSocket Session")
|
||||
|
@ -136,8 +137,8 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
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");
|
||||
Objects.requireNonNull(containerScope, "Container Scope cannot be null");
|
||||
Objects.requireNonNull(requestURI, "Request URI cannot be null");
|
||||
|
||||
this.classLoader = Thread.currentThread().getContextClassLoader();
|
||||
this.containerScope = containerScope;
|
||||
|
@ -160,13 +161,13 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
if (endpoint instanceof WebSocketConnectionListener)
|
||||
{
|
||||
WebSocketConnectionListener wslistener = (WebSocketConnectionListener)endpoint;
|
||||
WebSocketConnectionListener wslistener = (WebSocketConnectionListener) endpoint;
|
||||
onOpenFunction = (sess) -> {
|
||||
wslistener.onWebSocketConnect(sess);
|
||||
return null;
|
||||
};
|
||||
onCloseFunction = (closeinfo) -> {
|
||||
wslistener.onWebSocketClose(closeinfo.getStatusCode(),closeinfo.getReason());
|
||||
wslistener.onWebSocketClose(closeinfo.getStatusCode(), closeinfo.getReason());
|
||||
return null;
|
||||
};
|
||||
onErrorFunction = (cause) -> {
|
||||
|
@ -179,13 +180,13 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
if (endpoint instanceof WebSocketListener)
|
||||
{
|
||||
WebSocketListener wslistener = (WebSocketListener)endpoint;
|
||||
onTextSink = new StringMessageSink(policy,(payload) -> {
|
||||
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);
|
||||
onBinarySink = new ByteArrayMessageSink(policy, (payload) -> {
|
||||
wslistener.onWebSocketBinary(payload, 0, payload.length);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
@ -194,7 +195,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
if (endpoint instanceof WebSocketPingPongListener)
|
||||
{
|
||||
WebSocketPingPongListener wslistener = (WebSocketPingPongListener)endpoint;
|
||||
WebSocketPingPongListener wslistener = (WebSocketPingPongListener) endpoint;
|
||||
onPongFunction = (pong) -> {
|
||||
ByteBuffer payload = pong;
|
||||
if (pong == null)
|
||||
|
@ -215,21 +216,21 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
if (endpoint instanceof WebSocketPartialListener)
|
||||
{
|
||||
for(Method method: WebSocketPartialListener.class.getDeclaredMethods())
|
||||
for (Method method : WebSocketPartialListener.class.getDeclaredMethods())
|
||||
{
|
||||
if(method.getName().equals("onWebSocketPartialText"))
|
||||
if (method.getName().equals("onWebSocketPartialText"))
|
||||
assertNotSet(onTextSink, "TEXT Message Handler", endpoint.getClass(), method);
|
||||
else if(method.getName().equals("onWebSocketPartialBinary"))
|
||||
else if (method.getName().equals("onWebSocketPartialBinary"))
|
||||
assertNotSet(onBinarySink, "BINARY Message Handler", endpoint.getClass(), method);
|
||||
}
|
||||
|
||||
WebSocketPartialListener wslistener = (WebSocketPartialListener)endpoint;
|
||||
WebSocketPartialListener wslistener = (WebSocketPartialListener) endpoint;
|
||||
onTextSink = new PartialTextMessageSink((partial) -> {
|
||||
wslistener.onWebSocketPartialText(partial.getPayload(),partial.isFin());
|
||||
wslistener.onWebSocketPartialText(partial.getPayload(), partial.isFin());
|
||||
return null;
|
||||
});
|
||||
onBinarySink = new PartialBinaryMessageSink((partial) -> {
|
||||
wslistener.onWebSocketPartialBinary(partial.getPayload(),partial.isFin());
|
||||
wslistener.onWebSocketPartialBinary(partial.getPayload(), partial.isFin());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
@ -238,7 +239,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
if (endpoint instanceof WebSocketFrameListener)
|
||||
{
|
||||
WebSocketFrameListener wslistener = (WebSocketFrameListener)endpoint;
|
||||
WebSocketFrameListener wslistener = (WebSocketFrameListener) endpoint;
|
||||
onFrameFunction = (frame) -> {
|
||||
wslistener.onWebSocketFrame(new ReadOnlyDelegatedFrame(frame));
|
||||
return null;
|
||||
|
@ -261,35 +262,35 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
Method onmethod = null;
|
||||
|
||||
// OnWebSocketConnect [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketConnect.class);
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketConnect.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
assertNotSet(onOpenFunction, "Open/Connect Handler", endpointClass, onmethod);
|
||||
onOpenFunction = new OnOpenFunction(endpoint,onmethod);
|
||||
onOpenFunction = new OnOpenFunction(endpoint, onmethod);
|
||||
}
|
||||
// OnWebSocketClose [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketClose.class);
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketClose.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
assertNotSet(onCloseFunction, "Close Handler", endpointClass, onmethod);
|
||||
onCloseFunction = new OnCloseFunction(this,endpoint,onmethod);
|
||||
onCloseFunction = new OnCloseFunction(this, endpoint, onmethod);
|
||||
}
|
||||
// OnWebSocketError [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketError.class);
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketError.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
assertNotSet(onErrorFunction, "Error Handler", endpointClass, onmethod);
|
||||
onErrorFunction = new OnErrorFunction(this,endpoint,onmethod);
|
||||
onErrorFunction = new OnErrorFunction(this, endpoint, onmethod);
|
||||
}
|
||||
// OnWebSocketFrame [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass,OnWebSocketFrame.class);
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketFrame.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
assertNotSet(onFrameFunction, "Frame Handler", endpointClass, onmethod);
|
||||
onFrameFunction = new OnFrameFunction(this,endpoint,onmethod);
|
||||
onFrameFunction = new OnFrameFunction(this, endpoint, onmethod);
|
||||
}
|
||||
// OnWebSocketMessage [0..2]
|
||||
Method onmessages[] = ReflectUtils.findAnnotatedMethods(endpointClass,OnWebSocketMessage.class);
|
||||
Method onmessages[] = ReflectUtils.findAnnotatedMethods(endpointClass, OnWebSocketMessage.class);
|
||||
if (onmessages != null && onmessages.length > 0)
|
||||
{
|
||||
for (Method onmsg : onmessages)
|
||||
|
@ -298,36 +299,36 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
{
|
||||
assertNotSet(onTextSink, "TEXT Message Handler", endpointClass, onmsg);
|
||||
// Normal Text Message
|
||||
onTextSink = new StringMessageSink(policy,new OnTextFunction(this,endpoint,onmsg));
|
||||
onTextSink = new StringMessageSink(policy, new OnTextFunction(this, endpoint, onmsg));
|
||||
}
|
||||
else if (OnByteBufferFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
assertNotSet(onBinarySink, "Binary Message Handler", endpointClass, onmsg);
|
||||
// ByteBuffer Binary Message
|
||||
onBinarySink = new ByteBufferMessageSink(policy,new OnByteBufferFunction(this,endpoint,onmsg));
|
||||
onBinarySink = new ByteBufferMessageSink(policy, new OnByteBufferFunction(this, endpoint, onmsg));
|
||||
}
|
||||
else if (OnByteArrayFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
assertNotSet(onBinarySink, "Binary Message Handler", endpointClass, onmsg);
|
||||
// byte[] Binary Message
|
||||
onBinarySink = new ByteArrayMessageSink(policy,new OnByteArrayFunction(this,endpoint,onmsg));
|
||||
onBinarySink = new ByteArrayMessageSink(policy, new OnByteArrayFunction(this, endpoint, onmsg));
|
||||
}
|
||||
else if (OnInputStreamFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
assertNotSet(onBinarySink, "Binary Message Handler", endpointClass, onmsg);
|
||||
// InputStream Binary Message
|
||||
onBinarySink = new InputStreamMessageSink(executor,new OnInputStreamFunction(this,endpoint,onmsg));
|
||||
onBinarySink = new InputStreamMessageSink(executor, new OnInputStreamFunction(this, endpoint, onmsg));
|
||||
}
|
||||
else if (OnReaderFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
assertNotSet(onTextSink, "TEXT Message Handler", endpointClass, onmsg);
|
||||
// Reader Text Message
|
||||
onTextSink = new ReaderMessageSink(executor,new OnReaderFunction(this,endpoint,onmsg));
|
||||
onTextSink = new ReaderMessageSink(executor, new OnReaderFunction(this, endpoint, onmsg));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a valid @OnWebSocketMessage declaration signature
|
||||
throw InvalidSignatureException.build(onmsg,OnWebSocketMessage.class,
|
||||
throw InvalidSignatureException.build(onmsg, OnWebSocketMessage.class,
|
||||
OnTextFunction.getDynamicArgsBuilder(),
|
||||
OnByteBufferFunction.getDynamicArgsBuilder(),
|
||||
OnByteArrayFunction.getDynamicArgsBuilder(),
|
||||
|
@ -341,14 +342,14 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
|
||||
protected void assertNotSet(Object val, String role, Class<?> pojo, Method method)
|
||||
{
|
||||
if(val == null)
|
||||
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);
|
||||
ReflectUtils.append(err, pojo, method);
|
||||
|
||||
throw new InvalidWebSocketException(err.toString());
|
||||
}
|
||||
|
@ -357,19 +358,19 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
public void close()
|
||||
{
|
||||
/* This is assumed to always be a NORMAL closure, no reason phrase */
|
||||
connection.close(StatusCode.NORMAL,null);
|
||||
connection.close(StatusCode.NORMAL, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(CloseStatus closeStatus)
|
||||
{
|
||||
close(closeStatus.getCode(),closeStatus.getPhrase());
|
||||
this.close(closeStatus.getCode(), closeStatus.getPhrase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(int statusCode, String reason)
|
||||
{
|
||||
connection.close(statusCode,reason);
|
||||
connection.close(statusCode, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -381,7 +382,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
connection.disconnect();
|
||||
|
||||
// notify of harsh disconnect
|
||||
notifyClose(StatusCode.NO_CLOSE,"Harsh disconnect");
|
||||
notifyClose(StatusCode.NO_CLOSE, "Harsh disconnect");
|
||||
}
|
||||
|
||||
public void dispatch(Runnable runnable)
|
||||
|
@ -393,7 +394,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
protected void doStart() throws Exception
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("starting - {}",this);
|
||||
LOG.debug("starting - {}", this);
|
||||
|
||||
Iterator<RemoteEndpointFactory> iter = ServiceLoader.load(RemoteEndpointFactory.class).iterator();
|
||||
if (iter.hasNext())
|
||||
|
@ -412,15 +413,18 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
protected void doStop() throws Exception
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("stopping - {}",this);
|
||||
LOG.debug("stopping - {}", this);
|
||||
|
||||
if (getConnection() != null)
|
||||
{
|
||||
close(StatusCode.SHUTDOWN,"Shutdown");
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.debug("During Connection Shutdown",t);
|
||||
try
|
||||
{
|
||||
getConnection().close(StatusCode.SHUTDOWN, "Shutdown");
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.debug("During Connection Shutdown", t);
|
||||
}
|
||||
}
|
||||
super.doStop();
|
||||
}
|
||||
|
@ -433,7 +437,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
out.append(indent).append(" +- outgoingHandler : ");
|
||||
if (outgoingHandler instanceof Dumpable)
|
||||
{
|
||||
((Dumpable)outgoingHandler).dump(out,indent + " ");
|
||||
((Dumpable) outgoingHandler).dump(out, indent + " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -456,7 +460,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
{
|
||||
return false;
|
||||
}
|
||||
WebSocketSession other = (WebSocketSession)obj;
|
||||
WebSocketSession other = (WebSocketSession) obj;
|
||||
if (connection == null)
|
||||
{
|
||||
if (other.connection != null)
|
||||
|
@ -555,7 +559,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
public RemoteEndpoint getRemote()
|
||||
{
|
||||
if (LOG_OPEN.isDebugEnabled())
|
||||
LOG_OPEN.debug("[{}] {}.getRemote()",policy.getBehavior(),this.getClass().getSimpleName());
|
||||
LOG_OPEN.debug("[{}] {}.getRemote()", policy.getBehavior(), this.getClass().getSimpleName());
|
||||
ConnectionState state = connection.getIOState().getConnectionState();
|
||||
|
||||
if ((state == ConnectionState.OPEN) || (state == ConnectionState.CONNECTED))
|
||||
|
@ -636,8 +640,8 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
case OpCode.CLOSE:
|
||||
{
|
||||
boolean validate = true;
|
||||
CloseFrame closeframe = (CloseFrame)frame;
|
||||
CloseInfo close = new CloseInfo(closeframe,validate);
|
||||
CloseFrame closeframe = (CloseFrame) frame;
|
||||
CloseInfo close = new CloseInfo(closeframe, validate);
|
||||
|
||||
// process handshake
|
||||
getConnection().getIOState().onCloseRemote(close);
|
||||
|
@ -647,14 +651,14 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
case OpCode.PING:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("PING: {}",BufferUtil.toDetailString(frame.getPayload()));
|
||||
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);
|
||||
BufferUtil.put(frame.getPayload().slice(), pongBuf);
|
||||
BufferUtil.flipToFlush(pongBuf, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -670,7 +674,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
case OpCode.PONG:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("PONG: {}",BufferUtil.toDetailString(frame.getPayload()));
|
||||
LOG.debug("PONG: {}", BufferUtil.toDetailString(frame.getPayload()));
|
||||
|
||||
if (onPongFunction != null)
|
||||
onPongFunction.apply(frame.getPayload());
|
||||
|
@ -682,7 +686,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
activeMessageSink = onBinarySink;
|
||||
|
||||
if (activeMessageSink != null)
|
||||
activeMessageSink.accept(frame.getPayload(),frame.isFin());
|
||||
activeMessageSink.accept(frame.getPayload(), frame.isFin());
|
||||
return;
|
||||
}
|
||||
case OpCode.TEXT:
|
||||
|
@ -691,20 +695,20 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
activeMessageSink = onTextSink;
|
||||
|
||||
if (activeMessageSink != null)
|
||||
activeMessageSink.accept(frame.getPayload(),frame.isFin());
|
||||
activeMessageSink.accept(frame.getPayload(), frame.isFin());
|
||||
return;
|
||||
}
|
||||
case OpCode.CONTINUATION:
|
||||
{
|
||||
if (activeMessageSink != null)
|
||||
activeMessageSink.accept(frame.getPayload(),frame.isFin());
|
||||
activeMessageSink.accept(frame.getPayload(), frame.isFin());
|
||||
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unhandled OpCode: {}",opcode);
|
||||
LOG.debug("Unhandled OpCode: {}", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -717,17 +721,17 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
catch (NotUtf8Exception e)
|
||||
{
|
||||
notifyError(e);
|
||||
close(StatusCode.BAD_PAYLOAD,e.getMessage());
|
||||
close(StatusCode.BAD_PAYLOAD, e.getMessage());
|
||||
}
|
||||
catch (CloseException e)
|
||||
{
|
||||
close(e.getStatusCode(),e.getMessage());
|
||||
close(e.getStatusCode(), e.getMessage());
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
Throwable cause = getInvokedCause(t);
|
||||
|
||||
LOG.warn("Unhandled Error (closing connection)",cause);
|
||||
LOG.warn("Unhandled Error (closing connection)", cause);
|
||||
|
||||
notifyError(cause);
|
||||
|
||||
|
@ -735,10 +739,10 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
switch (policy.getBehavior())
|
||||
{
|
||||
case SERVER:
|
||||
close(StatusCode.SERVER_ERROR,cause.getClass().getSimpleName());
|
||||
close(StatusCode.SERVER_ERROR, cause.getClass().getSimpleName());
|
||||
break;
|
||||
case CLIENT:
|
||||
close(StatusCode.POLICY_VIOLATION,cause.getClass().getSimpleName());
|
||||
close(StatusCode.POLICY_VIOLATION, cause.getClass().getSimpleName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -779,10 +783,10 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("notifyClose({},{})",statusCode,reason);
|
||||
LOG.debug("notifyClose({},{})", statusCode, reason);
|
||||
}
|
||||
if (onCloseFunction != null)
|
||||
onCloseFunction.apply(new CloseInfo(statusCode,reason));
|
||||
onCloseFunction.apply(new CloseInfo(statusCode, reason));
|
||||
}
|
||||
|
||||
public void notifyError(Throwable cause)
|
||||
|
@ -799,7 +803,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
public void onOpened(Connection connection)
|
||||
{
|
||||
if (LOG_OPEN.isDebugEnabled())
|
||||
LOG_OPEN.debug("[{}] {}.onOpened()",policy.getBehavior(),this.getClass().getSimpleName());
|
||||
LOG_OPEN.debug("[{}] {}.onOpened()", policy.getBehavior(), this.getClass().getSimpleName());
|
||||
open();
|
||||
}
|
||||
|
||||
|
@ -813,11 +817,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
IOState ioState = this.connection.getIOState();
|
||||
CloseInfo close = ioState.getCloseInfo();
|
||||
// confirmed close of local endpoint
|
||||
notifyClose(close.getStatusCode(),close.getReason());
|
||||
notifyClose(close.getStatusCode(), close.getReason());
|
||||
try
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}.onSessionClosed()",containerScope.getClass().getSimpleName());
|
||||
LOG.debug("{}.onSessionClosed()", containerScope.getClass().getSimpleName());
|
||||
containerScope.onSessionClosed(this);
|
||||
}
|
||||
catch (Throwable t)
|
||||
|
@ -830,7 +834,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
try
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}.onSessionOpened()",containerScope.getClass().getSimpleName());
|
||||
LOG.debug("{}.onSessionOpened()", containerScope.getClass().getSimpleName());
|
||||
containerScope.onSessionOpened(this);
|
||||
}
|
||||
catch (Throwable t)
|
||||
|
@ -896,7 +900,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
{
|
||||
statusCode = StatusCode.POLICY_VIOLATION;
|
||||
}
|
||||
close(statusCode,cause.getMessage());
|
||||
close(statusCode, cause.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -936,11 +940,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
List<String> values = entry.getValue();
|
||||
if (values != null)
|
||||
{
|
||||
this.parameterMap.put(entry.getKey(),values.toArray(new String[values.size()]));
|
||||
this.parameterMap.put(entry.getKey(), values.toArray(new String[values.size()]));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.parameterMap.put(entry.getKey(),new String[0]);
|
||||
this.parameterMap.put(entry.getKey(), new String[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -968,15 +972,32 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("WebSocketSession[");
|
||||
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(",outgoing=").append(outgoingHandler);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.getClass().getSimpleName());
|
||||
sb.append('[');
|
||||
sb.append(getPolicy().getBehavior());
|
||||
Object endp = endpoint;
|
||||
// unwrap
|
||||
while (endp instanceof ManagedEndpoint)
|
||||
{
|
||||
endp = ((ManagedEndpoint) endp).getRawEndpoint();
|
||||
}
|
||||
sb.append(',').append(endp.getClass().getName());
|
||||
sb.append(',').append(getConnection().getClass().getSimpleName());
|
||||
if (getConnection() instanceof AbstractWebSocketConnection)
|
||||
{
|
||||
if(getConnection().getIOState().isOpen() && remote != null)
|
||||
{
|
||||
sb.append(',').append(getRemoteAddress());
|
||||
if (getPolicy().getBehavior() == WebSocketBehavior.SERVER)
|
||||
{
|
||||
sb.append(',').append(getRequestURI());
|
||||
sb.append(',').append(getLocalAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static interface Listener
|
||||
|
|
|
@ -16,22 +16,29 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.encoders;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
public class DefaultBinaryStreamEncoder extends AbstractEncoder implements Encoder.BinaryStream<ByteBuffer>
|
||||
public class AndPredicates<T> implements Predicate<T>
|
||||
{
|
||||
@Override
|
||||
public void encode(ByteBuffer message, OutputStream out) throws EncodeException, IOException
|
||||
private final Predicate<T> predicates[];
|
||||
|
||||
public AndPredicates(Predicate<T>... predicates)
|
||||
{
|
||||
BufferUtil.writeTo(message,out);
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t)
|
||||
{
|
||||
for (Predicate<T> predicate : predicates)
|
||||
{
|
||||
if (!predicate.test(t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AnnotationPredicate implements Predicate<Method>
|
||||
{
|
||||
private static final Map<Class<? extends Annotation>, Predicate<Method>>
|
||||
CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Get Predicate from Cache (add if not present)
|
||||
*/
|
||||
public static Predicate<Method> get(Class<? extends Annotation> annotation)
|
||||
{
|
||||
Predicate<Method> predicate = CACHE.get(annotation);
|
||||
if (predicate == null)
|
||||
{
|
||||
predicate = new AnnotationPredicate(annotation);
|
||||
CACHE.put(annotation, predicate);
|
||||
}
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private final Class<? extends Annotation> annotation;
|
||||
|
||||
public AnnotationPredicate(Class<? extends Annotation> annotation)
|
||||
{
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Method method)
|
||||
{
|
||||
return (method.getAnnotation(annotation) != null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,495 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
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.InvalidWebSocketException;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
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.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.Frame;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.ManagedEndpoint;
|
||||
import org.eclipse.jetty.websocket.common.frames.ReadOnlyDelegatedFrame;
|
||||
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.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
* The Common Implementation of EndpointFunctions
|
||||
*
|
||||
* @param <T> the Session object
|
||||
*/
|
||||
public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycle implements EndpointFunctions<T>
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(CommonEndpointFunctions.class);
|
||||
|
||||
protected final Object endpoint;
|
||||
protected final WebSocketPolicy policy;
|
||||
protected final Executor executor;
|
||||
|
||||
private T session;
|
||||
private Function<T, Void> onOpenFunction;
|
||||
private Function<CloseInfo, Void> onCloseFunction;
|
||||
private Function<Throwable, Void> onErrorFunction;
|
||||
private Function<Frame, Void> onFrameFunction;
|
||||
private Function<ByteBuffer, Void> onPingFunction;
|
||||
private Function<ByteBuffer, Void> onPongFunction;
|
||||
|
||||
private MessageSink onTextSink;
|
||||
private MessageSink onBinarySink;
|
||||
|
||||
private BatchMode batchMode;
|
||||
|
||||
public CommonEndpointFunctions(Object endpoint, WebSocketPolicy policy, Executor executor)
|
||||
{
|
||||
Object e = endpoint;
|
||||
// unwrap endpoint
|
||||
while (e instanceof ManagedEndpoint)
|
||||
e = ((ManagedEndpoint) e).getRawEndpoint();
|
||||
|
||||
Objects.requireNonNull(endpoint, "Endpoint cannot be null");
|
||||
Objects.requireNonNull(policy, "WebSocketPolicy cannot be null");
|
||||
Objects.requireNonNull(executor, "Executor cannot be null");
|
||||
this.endpoint = e;
|
||||
this.policy = policy;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
super.doStart();
|
||||
discoverEndpointFunctions(this.endpoint);
|
||||
}
|
||||
|
||||
protected void discoverEndpointFunctions(Object endpoint)
|
||||
{
|
||||
boolean supportAnnotations = true;
|
||||
|
||||
// Connection Listener
|
||||
if (endpoint instanceof WebSocketConnectionListener)
|
||||
{
|
||||
WebSocketConnectionListener listener = (WebSocketConnectionListener) endpoint;
|
||||
setOnOpen((session) -> {
|
||||
listener.onWebSocketConnect(session);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketConnect", Session.class)
|
||||
);
|
||||
setOnClose((close) -> {
|
||||
listener.onWebSocketClose(close.getStatusCode(), close.getReason());
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketClose", int.class, String.class)
|
||||
);
|
||||
setOnError((cause) -> {
|
||||
listener.onWebSocketError(cause);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketError", Throwable.class));
|
||||
supportAnnotations = false;
|
||||
}
|
||||
|
||||
// Simple Data Listener
|
||||
if (endpoint instanceof WebSocketListener)
|
||||
{
|
||||
WebSocketListener listener = (WebSocketListener) endpoint;
|
||||
|
||||
setOnText(new StringMessageSink(policy, (payload) -> {
|
||||
listener.onWebSocketText(payload);
|
||||
return null;
|
||||
}),
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketText", String.class));
|
||||
setOnBinary(new ByteArrayMessageSink(policy, (payload) -> {
|
||||
listener.onWebSocketBinary(payload, 0, payload.length);
|
||||
return null;
|
||||
}),
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketBinary", byte[].class, int.class, int.class));
|
||||
supportAnnotations = false;
|
||||
}
|
||||
|
||||
// Ping/Pong Listener
|
||||
if (endpoint instanceof WebSocketPingPongListener)
|
||||
{
|
||||
WebSocketPingPongListener listener = (WebSocketPingPongListener) endpoint;
|
||||
setOnPong((pong) -> {
|
||||
ByteBuffer payload = pong;
|
||||
if (pong == null)
|
||||
payload = BufferUtil.EMPTY_BUFFER;
|
||||
listener.onWebSocketPong(payload);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketPong", ByteBuffer.class));
|
||||
setOnPing((ping) -> {
|
||||
ByteBuffer payload = ping;
|
||||
if (ping == null)
|
||||
payload = BufferUtil.EMPTY_BUFFER;
|
||||
listener.onWebSocketPing(payload);
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketPing", ByteBuffer.class));
|
||||
supportAnnotations = false;
|
||||
}
|
||||
|
||||
// Partial Data / Message Listener
|
||||
if (endpoint instanceof WebSocketPartialListener)
|
||||
{
|
||||
WebSocketPartialListener listener = (WebSocketPartialListener) endpoint;
|
||||
setOnText(new PartialTextMessageSink((partial) -> {
|
||||
listener.onWebSocketPartialText(partial.getPayload(), partial.isFin());
|
||||
return null;
|
||||
}),
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketPartialText", String.class, boolean.class));
|
||||
setOnBinary(new PartialBinaryMessageSink((partial) -> {
|
||||
listener.onWebSocketPartialBinary(partial.getPayload(), partial.isFin());
|
||||
return null;
|
||||
}),
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketPartialBinary", ByteBuffer.class, boolean.class));
|
||||
supportAnnotations = false;
|
||||
}
|
||||
|
||||
// Frame Listener
|
||||
if (endpoint instanceof WebSocketFrameListener)
|
||||
{
|
||||
WebSocketFrameListener listener = (WebSocketFrameListener) endpoint;
|
||||
setOnFrame((frame) -> {
|
||||
listener.onWebSocketFrame(new ReadOnlyDelegatedFrame(frame));
|
||||
return null;
|
||||
},
|
||||
ReflectUtils.findMethod(endpoint.getClass(), "onWebSocketFrame", Frame.class));
|
||||
supportAnnotations = false;
|
||||
}
|
||||
|
||||
if (supportAnnotations)
|
||||
discoverAnnotatedEndpointFunctions(endpoint);
|
||||
}
|
||||
|
||||
protected void discoverAnnotatedEndpointFunctions(Object endpoint)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
setOnOpen(new OnOpenFunction(endpoint, onmethod), onmethod);
|
||||
}
|
||||
// OnWebSocketClose [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketClose.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
setOnClose(new OnCloseFunction(session, endpoint, onmethod), onmethod);
|
||||
}
|
||||
// OnWebSocketError [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketError.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
setOnError(new OnErrorFunction(session, endpoint, onmethod), onmethod);
|
||||
}
|
||||
// OnWebSocketFrame [0..1]
|
||||
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketFrame.class);
|
||||
if (onmethod != null)
|
||||
{
|
||||
setOnFrame(new OnFrameFunction(session, endpoint, onmethod), 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))
|
||||
{
|
||||
// Normal Text Message
|
||||
setOnText(new StringMessageSink(policy, new OnTextFunction(session, endpoint, onmsg)), onmsg);
|
||||
}
|
||||
else if (OnByteBufferFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
// ByteBuffer Binary Message
|
||||
setOnBinary(new ByteBufferMessageSink(policy, new OnByteBufferFunction(session, endpoint, onmsg)), onmsg);
|
||||
}
|
||||
else if (OnByteArrayFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
// byte[] Binary Message
|
||||
setOnBinary(new ByteArrayMessageSink(policy, new OnByteArrayFunction(session, endpoint, onmsg)), onmsg);
|
||||
}
|
||||
else if (OnInputStreamFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
// InputStream Binary Message
|
||||
setOnBinary(new InputStreamMessageSink(executor, new OnInputStreamFunction(session, endpoint, onmsg)), onmsg);
|
||||
}
|
||||
else if (OnReaderFunction.hasMatchingSignature(onmsg))
|
||||
{
|
||||
// Reader Text Message
|
||||
setOnText(new ReaderMessageSink(executor, new OnReaderFunction(session, endpoint, onmsg)), onmsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a valid @OnWebSocketMessage declaration signature
|
||||
throw InvalidSignatureException.build(onmsg, OnWebSocketMessage.class,
|
||||
OnTextFunction.getDynamicArgsBuilder(),
|
||||
OnByteBufferFunction.getDynamicArgsBuilder(),
|
||||
OnByteArrayFunction.getDynamicArgsBuilder(),
|
||||
OnInputStreamFunction.getDynamicArgsBuilder(),
|
||||
OnReaderFunction.getDynamicArgsBuilder());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BatchMode getBatchMode()
|
||||
{
|
||||
return batchMode;
|
||||
}
|
||||
|
||||
public T getSession()
|
||||
{
|
||||
return session;
|
||||
}
|
||||
|
||||
public void setOnOpen(Function<T, Void> function, Object origin)
|
||||
{
|
||||
assertNotSet(this.onOpenFunction, "Open Handler", origin);
|
||||
this.onOpenFunction = function;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onOpen to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnClose(Function<CloseInfo, Void> function, Object origin)
|
||||
{
|
||||
assertNotSet(this.onCloseFunction, "Close Handler", origin);
|
||||
this.onCloseFunction = function;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onClose to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnError(Function<Throwable, Void> function, Object origin)
|
||||
{
|
||||
assertNotSet(this.onErrorFunction, "Error Handler", origin);
|
||||
this.onErrorFunction = function;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onError to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnText(MessageSink messageSink, Object origin)
|
||||
{
|
||||
assertNotSet(this.onTextSink, "TEXT Handler", origin);
|
||||
this.onTextSink = messageSink;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onText to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnBinary(MessageSink messageSink, Object origin)
|
||||
{
|
||||
assertNotSet(this.onBinarySink, "BINARY Handler", origin);
|
||||
this.onBinarySink = messageSink;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onBinary to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnFrame(Function<Frame, Void> function, Object origin)
|
||||
{
|
||||
assertNotSet(this.onFrameFunction, "Frame Handler", origin);
|
||||
this.onFrameFunction = function;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onFrame to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnPing(Function<ByteBuffer, Void> function, Object origin)
|
||||
{
|
||||
assertNotSet(this.onPingFunction, "Ping Handler", origin);
|
||||
this.onPingFunction = function;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onPing to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnPong(Function<ByteBuffer, Void> function, Object origin)
|
||||
{
|
||||
assertNotSet(this.onPongFunction, "Pong Handler", origin);
|
||||
this.onPongFunction = function;
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Assigned onPong to " + describeOrigin(origin));
|
||||
}
|
||||
}
|
||||
|
||||
private String describeOrigin(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return "<undefined>";
|
||||
}
|
||||
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
protected void assertNotSet(Object val, String role, Object origin)
|
||||
{
|
||||
if (val == null)
|
||||
return;
|
||||
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Cannot replace previously assigned ");
|
||||
err.append(role);
|
||||
err.append(" with ");
|
||||
err.append(describeOrigin(origin));
|
||||
|
||||
throw new InvalidWebSocketException(err.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(T session)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
this.session = session;
|
||||
|
||||
if (onOpenFunction != null)
|
||||
onOpenFunction.apply(this.session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CloseInfo close)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onCloseFunction != null)
|
||||
onCloseFunction.apply(close);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrame(Frame frame)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onFrameFunction != null)
|
||||
onFrameFunction.apply(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable cause)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onErrorFunction != null)
|
||||
onErrorFunction.apply(cause);
|
||||
else
|
||||
LOG.debug(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onText(ByteBuffer payload, boolean fin)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onTextSink != null)
|
||||
onTextSink.accept(payload, fin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBinary(ByteBuffer payload, boolean fin)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onBinarySink != null)
|
||||
onBinarySink.accept(payload, fin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPing(ByteBuffer payload)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onPingFunction != null)
|
||||
onPingFunction.apply(payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPong(ByteBuffer payload)
|
||||
{
|
||||
if (!isStarted())
|
||||
throw new IllegalStateException(this.getClass().getName() + " not started");
|
||||
|
||||
if (onPongFunction != null)
|
||||
onPongFunction.apply(payload);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
|
||||
/**
|
||||
* The interface a WebSocket Connection and Session has to the User provided Endpoint.
|
||||
*
|
||||
* @param <T> the Session object
|
||||
*/
|
||||
public interface EndpointFunctions<T>
|
||||
{
|
||||
void onOpen(T session);
|
||||
|
||||
void onClose(CloseInfo close);
|
||||
|
||||
void onFrame(Frame frame);
|
||||
|
||||
void onError(Throwable cause);
|
||||
|
||||
void onText(ByteBuffer payload, boolean fin);
|
||||
|
||||
void onBinary(ByteBuffer payload, boolean fin);
|
||||
|
||||
void onPing(ByteBuffer payload);
|
||||
|
||||
void onPong(ByteBuffer payload);
|
||||
}
|
|
@ -16,20 +16,18 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
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.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -87,14 +85,7 @@ public class OnByteArrayFunction implements Function<byte[], Void>
|
|||
@Override
|
||||
public Void apply(byte[] bin)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, this.session, bin, 0, bin.length);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, this.session, bin, 0, bin.length);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,9 +16,8 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Function;
|
||||
|
@ -26,11 +25,10 @@ import java.util.function.Function;
|
|||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -84,14 +82,7 @@ public class OnByteBufferFunction implements Function<ByteBuffer, Void>
|
|||
@Override
|
||||
public Void apply(ByteBuffer bin)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, bin);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, bin);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,9 +16,8 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -26,11 +25,10 @@ import org.eclipse.jetty.websocket.api.Session;
|
|||
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.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -77,14 +75,7 @@ public class OnCloseFunction implements Function<CloseInfo, Void>
|
|||
@Override
|
||||
public Void apply(CloseInfo closeinfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, closeinfo.getStatusCode(), closeinfo.getReason());
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call close method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, closeinfo.getStatusCode(), closeinfo.getReason());
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,20 +16,18 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
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.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -73,14 +71,7 @@ public class OnErrorFunction implements Function<Throwable, Void>
|
|||
@Override
|
||||
public Void apply(Throwable cause)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, cause);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call error method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, cause);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,9 +16,8 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -26,12 +25,11 @@ import org.eclipse.jetty.websocket.api.Session;
|
|||
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.FunctionCallException;
|
||||
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.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -76,14 +74,7 @@ public class OnFrameFunction implements Function<Frame, Void>
|
|||
public Void apply(Frame frame)
|
||||
{
|
||||
WebSocketFrame copy = WebSocketFrame.copy(frame);
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, copy);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call frame method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, copy);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,21 +16,19 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
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.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -85,14 +83,7 @@ public class OnInputStreamFunction implements Function<InputStream, Void>
|
|||
@Override
|
||||
public Void apply(InputStream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, stream);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, stream);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,26 +16,24 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
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.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
* Jetty {@link WebSocket} {@link OnWebSocketConnect} method {@link Function}
|
||||
*/
|
||||
public class OnOpenFunction implements Function<Session, Void>
|
||||
public class OnOpenFunction<T extends Session> implements Function<T, Void>
|
||||
{
|
||||
private static final DynamicArgs.Builder ARGBUILDER;
|
||||
private static final Arg ARG_SESSION = new Arg(1, Session.class);
|
||||
|
@ -70,14 +68,7 @@ public class OnOpenFunction implements Function<Session, Void>
|
|||
@Override
|
||||
public Void apply(Session session)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,21 +16,19 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
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.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -84,14 +82,7 @@ public class OnReaderFunction implements Function<Reader, Void>
|
|||
@Override
|
||||
public Void apply(Reader stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, stream);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, stream);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,20 +16,18 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.functions;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
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.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.FunctionCallException;
|
||||
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.Arg;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs;
|
||||
import org.eclipse.jetty.websocket.common.reflect.ExactSignature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
|
@ -83,14 +81,7 @@ public class OnTextFunction implements Function<String, Void>
|
|||
@Override
|
||||
public Void apply(String text)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.callable.invoke(endpoint, session, text);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
|
||||
}
|
||||
this.callable.invoke(endpoint, session, text);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PublicNonStaticPredicate implements Predicate<Method>
|
||||
{
|
||||
public static final Predicate<Method> INSTANCE = new PublicNonStaticPredicate();
|
||||
|
||||
@Override
|
||||
public boolean test(Method method)
|
||||
{
|
||||
int mods = method.getModifiers();
|
||||
if (!Modifier.isPublic(mods))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Modifier.isStatic(mods))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -16,20 +16,25 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.encoders;
|
||||
package org.eclipse.jetty.websocket.common.function;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
||||
public class DefaultTextStreamEncoder extends AbstractEncoder implements Encoder.TextStream<String>
|
||||
public class ReturnTypePredicate implements Predicate<Method>
|
||||
{
|
||||
@Override
|
||||
public void encode(String message, Writer writer) throws EncodeException, IOException
|
||||
public static final Predicate<Method> VOID = new ReturnTypePredicate(Void.TYPE);
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
public ReturnTypePredicate(Class<?> type)
|
||||
{
|
||||
writer.append(message);
|
||||
writer.flush();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Method method)
|
||||
{
|
||||
return type.equals(method.getReturnType());
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
@ -32,7 +33,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
|
||||
/**
|
||||
* Support class for reading a (single) WebSocket BINARY message via a InputStream.
|
||||
* <p>
|
||||
* <p/>
|
||||
* An InputStream that can access a queue of ByteBuffer payloads, along with expected InputStream blocking behavior.
|
||||
*/
|
||||
public class MessageInputStream extends InputStream implements MessageSink
|
||||
|
@ -41,8 +42,11 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
private static final ByteBuffer EOF = ByteBuffer.allocate(0).asReadOnlyBuffer();
|
||||
|
||||
private final BlockingDeque<ByteBuffer> buffers = new LinkedBlockingDeque<>();
|
||||
private AtomicBoolean closed = new AtomicBoolean(false);
|
||||
|
||||
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||
private final long timeoutMs;
|
||||
private final CountDownLatch closedLatch = new CountDownLatch(1);
|
||||
|
||||
private ByteBuffer activeBuffer = null;
|
||||
|
||||
public MessageInputStream()
|
||||
|
@ -60,7 +64,7 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Appending {} chunk: {}",fin?"final":"non-final",BufferUtil.toDetailString(payload));
|
||||
LOG.debug("Appending {} chunk: {}", fin ? "final" : "non-final", BufferUtil.toDetailString(payload));
|
||||
}
|
||||
|
||||
// If closed, we should just toss incoming payloads into the bit bucket.
|
||||
|
@ -87,7 +91,7 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
return;
|
||||
}
|
||||
// TODO: the copy buffer should be pooled too, but no buffer pool available from here.
|
||||
ByteBuffer copy = payload.isDirect()?ByteBuffer.allocateDirect(capacity):ByteBuffer.allocate(capacity);
|
||||
ByteBuffer copy = payload.isDirect() ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity);
|
||||
copy.put(payload).flip();
|
||||
buffers.put(copy);
|
||||
}
|
||||
|
@ -111,6 +115,7 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
{
|
||||
buffers.offer(EOF);
|
||||
super.close();
|
||||
closedLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +169,7 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
LOG.debug("Reached EOF");
|
||||
// Be sure that this stream cannot be reused.
|
||||
closed.set(true);
|
||||
closedLatch.countDown();
|
||||
// Removed buffers that may have remained in the queue.
|
||||
buffers.clear();
|
||||
return -1;
|
||||
|
@ -177,6 +183,7 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Interrupted while waiting to read", x);
|
||||
closed.set(true);
|
||||
closedLatch.countDown();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -186,4 +193,16 @@ public class MessageInputStream extends InputStream implements MessageSink
|
|||
{
|
||||
throw new IOException("reset() not supported");
|
||||
}
|
||||
|
||||
public void awaitClose()
|
||||
{
|
||||
try
|
||||
{
|
||||
closedLatch.await();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException("Stream Close wait interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,4 +42,9 @@ public class MessageReader extends InputStreamReader implements MessageSink
|
|||
{
|
||||
this.stream.accept(payload, fin);
|
||||
}
|
||||
|
||||
public void awaitClose()
|
||||
{
|
||||
stream.awaitClose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,15 +51,10 @@ public class ReaderMessageSink implements MessageSink
|
|||
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);
|
||||
}
|
||||
executor.execute(() -> {
|
||||
// processing of errors is the responsibility
|
||||
// of the stream function
|
||||
onStreamFunction.apply(stream);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +62,7 @@ public class ReaderMessageSink implements MessageSink
|
|||
{
|
||||
if (fin)
|
||||
{
|
||||
stream.awaitClose();
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.reflect;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A single argument for a method
|
||||
*/
|
||||
public class Arg
|
||||
{
|
||||
private final Class<?> type;
|
||||
private Method method;
|
||||
private int index;
|
||||
private Object tag;
|
||||
private boolean required = false;
|
||||
|
||||
public Arg(Class<?> type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Arg(int idx, Class<?> type)
|
||||
{
|
||||
this.index = idx;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Arg(Method method, int idx, Class<?> type)
|
||||
{
|
||||
this.method = method;
|
||||
this.index = idx;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annoClass)
|
||||
{
|
||||
if (method == null)
|
||||
return null;
|
||||
|
||||
Annotation annos[] = method.getParameterAnnotations()[index];
|
||||
if (annos != null || (annos.length > 0))
|
||||
{
|
||||
for (Annotation anno : annos)
|
||||
{
|
||||
if (anno.annotationType().equals(annoClass))
|
||||
{
|
||||
return (T) anno;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return type.getName();
|
||||
}
|
||||
|
||||
public Class<?> getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isArray()
|
||||
{
|
||||
return type.isArray();
|
||||
}
|
||||
|
||||
public boolean isRequired()
|
||||
{
|
||||
return required;
|
||||
}
|
||||
|
||||
public boolean matches(Arg other)
|
||||
{
|
||||
// If tags exist
|
||||
if (this.tag != null)
|
||||
{
|
||||
// They have to match
|
||||
return (this.tag.equals(other.tag));
|
||||
}
|
||||
|
||||
// Lastly, if types match, use em
|
||||
return (this.type.isAssignableFrom(other.type));
|
||||
}
|
||||
|
||||
public Arg required()
|
||||
{
|
||||
this.required = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Arg setTag(String tag)
|
||||
{
|
||||
this.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[%s%d%s]", type.getName(),
|
||||
required ? "!" : "", index, tag == null ? "" : "/" + tag);
|
||||
}
|
||||
}
|
|
@ -16,9 +16,9 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface ArgIdentifier extends Function<DynamicArgs.Arg,DynamicArgs.Arg> {
|
||||
public interface ArgIdentifier extends Function<Arg,Arg> {
|
||||
}
|
|
@ -16,16 +16,15 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.encoders;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import javax.websocket.EncodeException;
|
||||
import javax.websocket.Encoder;
|
||||
|
||||
public class DefaultTextEncoder extends AbstractEncoder implements Encoder.Text<String>
|
||||
/**
|
||||
* A set of potential arguments, only one of which is allowed
|
||||
*/
|
||||
public class ArgSwitch extends Arg
|
||||
{
|
||||
@Override
|
||||
public String encode(String message) throws EncodeException
|
||||
public ArgSwitch(Class<?> type)
|
||||
{
|
||||
return message;
|
||||
super(type);
|
||||
}
|
||||
}
|
|
@ -16,16 +16,14 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Provide argument utilities for working with methods that
|
||||
|
@ -39,107 +37,58 @@ import java.util.function.BiPredicate;
|
|||
*/
|
||||
public class DynamicArgs
|
||||
{
|
||||
public static interface Signature
|
||||
public interface Signature
|
||||
{
|
||||
/**
|
||||
* Predicate to test if signature matches
|
||||
*
|
||||
* @return the predicate to test if signature matches
|
||||
*/
|
||||
public BiPredicate<Method, Class<?>[]> getPredicate();
|
||||
Predicate<Method> getPredicate();
|
||||
|
||||
/**
|
||||
* Get Call Args
|
||||
*
|
||||
* @return the Call Args
|
||||
*/
|
||||
Arg[] getCallArgs();
|
||||
|
||||
/**
|
||||
* BiFunction to use to invoke method
|
||||
* against give object, with provided (potential) arguments,
|
||||
* returning appropriate result from invocation.
|
||||
*
|
||||
* @param method
|
||||
* the method to base BiFunction off of.
|
||||
* @param callArgs
|
||||
* the description of arguments passed into each {@link DynamicArgs#invoke(Object, Object...)}
|
||||
* call in the future. Used to map the incoming arguments to the method arguments.
|
||||
* @param method the method to base BiFunction off of.
|
||||
* @param callArgs the description of arguments passed into each {@link DynamicArgs#invoke(Object, Object...)}
|
||||
* call in the future. Used to map the incoming arguments to the method arguments.
|
||||
* @return the return result of the invoked method
|
||||
*/
|
||||
public BiFunction<Object, Object[], Object> getInvoker(Method method, DynamicArgs.Arg... callArgs);
|
||||
BiFunction<Object, Object[], Object> getInvoker(Method method, Arg... callArgs);
|
||||
|
||||
public void appendDescription(StringBuilder str);
|
||||
void appendDescription(StringBuilder str);
|
||||
}
|
||||
|
||||
public static class Arg
|
||||
{
|
||||
public final Class<?> type;
|
||||
public Method method;
|
||||
public int index;
|
||||
public Object tag;
|
||||
|
||||
public Arg(Class<?> type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Arg(int idx, Class<?> type)
|
||||
{
|
||||
this.index = idx;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Arg(Method method, int idx, Class<?> type)
|
||||
{
|
||||
this.method = method;
|
||||
this.index = idx;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Arg setTag(String tag)
|
||||
{
|
||||
this.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[%d%s]",type.getName(),index,tag == null ? "" : "/" + tag);
|
||||
}
|
||||
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annoClass)
|
||||
{
|
||||
if(method == null)
|
||||
return null;
|
||||
|
||||
Annotation annos[] = method.getParameterAnnotations()[index];
|
||||
if(annos != null || (annos.length > 0))
|
||||
{
|
||||
for(Annotation anno: annos)
|
||||
{
|
||||
if(anno.annotationType().equals(annoClass))
|
||||
{
|
||||
return (T) anno;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
public static class Builder implements Predicate<Method>
|
||||
{
|
||||
private List<Signature> signatures = new ArrayList<>();
|
||||
|
||||
public DynamicArgs build(Method method, Arg... callArgs)
|
||||
{
|
||||
// FIXME: add DynamicArgs build cache (key = method+callargs)
|
||||
Signature signature = getMatchingSignature(method);
|
||||
if (signature == null)
|
||||
return null;
|
||||
return build(method, signature);
|
||||
}
|
||||
|
||||
Class<?> paramTypes[] = method.getParameterTypes();
|
||||
for (Signature sig : signatures)
|
||||
{
|
||||
if (sig.getPredicate().test(method, paramTypes))
|
||||
{
|
||||
return new DynamicArgs(sig.getInvoker(method,callArgs));
|
||||
}
|
||||
}
|
||||
public DynamicArgs build(Method method, Signature signature)
|
||||
{
|
||||
return new DynamicArgs(signature.getInvoker(method, signature.getCallArgs()));
|
||||
}
|
||||
|
||||
return null;
|
||||
@Override
|
||||
public boolean test(Method method)
|
||||
{
|
||||
return hasMatchingSignature(method);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,18 +99,28 @@ public class DynamicArgs
|
|||
*/
|
||||
public boolean hasMatchingSignature(Method method)
|
||||
{
|
||||
// FIXME: add match cache (key = method)
|
||||
return getMatchingSignature(method) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Signature} that matches the method
|
||||
*
|
||||
* @param method the method to inspect
|
||||
* @return the Signature, or null if no signature match
|
||||
*/
|
||||
public Signature getMatchingSignature(Method method)
|
||||
{
|
||||
// FIXME: add match cache (key = method, value = signature)
|
||||
|
||||
Class<?> paramTypes[] = method.getParameterTypes();
|
||||
for (Signature sig : signatures)
|
||||
{
|
||||
if (sig.getPredicate().test(method, paramTypes))
|
||||
if (sig.getPredicate().test(method))
|
||||
{
|
||||
return true;
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Builder addSignature(Signature sig)
|
||||
|
@ -197,6 +156,14 @@ public class DynamicArgs
|
|||
return argIdentifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* BiFunction invoker
|
||||
* <ol>
|
||||
* <li>First Arg</li>
|
||||
* <li>Second Arg</li>
|
||||
* <li>Result Type</li>
|
||||
* </ol>
|
||||
*/
|
||||
private final BiFunction<Object, Object[], Object> invoker;
|
||||
|
||||
private DynamicArgs(BiFunction<Object, Object[], Object> invoker)
|
||||
|
@ -207,20 +174,12 @@ public class DynamicArgs
|
|||
/**
|
||||
* Invoke the signature / method with the provided potential args.
|
||||
*
|
||||
* @param o
|
||||
* the object to call method on
|
||||
* @param potentialArgs
|
||||
* the potential args in the same order as the FIXME
|
||||
* @param o the object to call method on
|
||||
* @param potentialArgs the potential args in the same order as the Call Args
|
||||
* @return the response object from the invoke
|
||||
* @throws IllegalAccessException
|
||||
* if unable to access the method or object
|
||||
* @throws IllegalArgumentException
|
||||
* if call to method has invalid/illegal arguments
|
||||
* @throws InvocationTargetException
|
||||
* if unable to invoke the method on the object
|
||||
*/
|
||||
public Object invoke(Object o, Object... potentialArgs) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
|
||||
public Object invoke(Object o, Object... potentialArgs)
|
||||
{
|
||||
return invoker.apply(o,potentialArgs);
|
||||
return invoker.apply(o, potentialArgs);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
|
@ -16,17 +16,17 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Signature;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs.Signature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
public class ExactSignature implements Signature, BiPredicate<Method,Class<?>[]>
|
||||
public class ExactSignature implements Signature, Predicate<Method>
|
||||
{
|
||||
private final Arg[] params;
|
||||
|
||||
|
@ -41,20 +41,27 @@ public class ExactSignature implements Signature, BiPredicate<Method,Class<?>[]>
|
|||
}
|
||||
|
||||
@Override
|
||||
public BiPredicate<Method,Class<?>[]> getPredicate()
|
||||
public Predicate<Method> getPredicate()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Method method, Class<?>[] types)
|
||||
public Arg[] getCallArgs()
|
||||
{
|
||||
return this.params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Method method)
|
||||
{
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
if (types.length != params.length)
|
||||
return false;
|
||||
int len = params.length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (!params[i].type.equals(types[i]))
|
||||
if (!params[i].getType().equals(types[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -71,8 +78,8 @@ public class ExactSignature implements Signature, BiPredicate<Method,Class<?>[]>
|
|||
str.append(',');
|
||||
}
|
||||
str.append(' ');
|
||||
str.append(arg.type.getName());
|
||||
if (arg.type.isArray())
|
||||
str.append(arg.getName());
|
||||
if (arg.isArray())
|
||||
{
|
||||
str.append("[]");
|
||||
}
|
||||
|
@ -82,7 +89,7 @@ public class ExactSignature implements Signature, BiPredicate<Method,Class<?>[]>
|
|||
}
|
||||
|
||||
@Override
|
||||
public BiFunction<Object, Object[], Object> getInvoker(Method method, DynamicArgs.Arg... callArgs)
|
||||
public BiFunction<Object, Object[], Object> getInvoker(Method method, Arg... callArgs)
|
||||
{
|
||||
// Figure out mapping of calling args to method args
|
||||
Class<?> paramTypes[] = method.getParameterTypes();
|
||||
|
@ -98,7 +105,7 @@ public class ExactSignature implements Signature, BiPredicate<Method,Class<?>[]>
|
|||
// Find reference to argument in callArgs
|
||||
for (int ci = 0; ci < callArgsLen; ci++)
|
||||
{
|
||||
if (callArgs[ci].index == params[mi].index)
|
||||
if (callArgs[ci].getIndex() == params[mi].getIndex())
|
||||
{
|
||||
ref = ci;
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
|
||||
|
@ -26,11 +26,11 @@ import org.eclipse.jetty.util.annotation.Name;
|
|||
public class NameArgIdentifier implements ArgIdentifier
|
||||
{
|
||||
@Override
|
||||
public DynamicArgs.Arg apply(DynamicArgs.Arg arg)
|
||||
public Arg apply(Arg arg)
|
||||
{
|
||||
Name name = arg.getAnnotation(Name.class);
|
||||
if (name != null)
|
||||
arg.tag = name.value();
|
||||
arg.setTag(name.value());
|
||||
return arg;
|
||||
}
|
||||
}
|
|
@ -16,19 +16,22 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Signature;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.common.reflect.DynamicArgs.Signature;
|
||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||
|
||||
public class UnorderedSignature implements Signature, BiPredicate<Method, Class<?>[]>
|
||||
public class UnorderedSignature implements Signature, Predicate<Method>
|
||||
{
|
||||
private final static Logger LOG = Log.getLogger(UnorderedSignature.class);
|
||||
private final Arg[] params;
|
||||
|
||||
public UnorderedSignature(Arg... args)
|
||||
|
@ -37,53 +40,21 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
}
|
||||
|
||||
@Override
|
||||
public BiPredicate<Method, Class<?>[]> getPredicate()
|
||||
public Arg[] getCallArgs()
|
||||
{
|
||||
return this.params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<Method> getPredicate()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Method method, Class<?>[] types)
|
||||
public boolean test(Method method)
|
||||
{
|
||||
// Matches if the provided types
|
||||
// match the valid params in any order
|
||||
|
||||
// Figure out mapping of calling args to method args
|
||||
Class<?> paramTypes[] = method.getParameterTypes();
|
||||
int paramTypesLength = paramTypes.length;
|
||||
|
||||
// Method argument array pointing to index in calling array
|
||||
int callArgsLen = params.length;
|
||||
|
||||
List<ArgIdentifier> argIdentifiers = DynamicArgs.lookupArgIdentifiers();
|
||||
|
||||
for (int mi = 0; mi < paramTypesLength; mi++)
|
||||
{
|
||||
DynamicArgs.Arg methodArg = new DynamicArgs.Arg(method, mi, paramTypes[mi]);
|
||||
|
||||
for (ArgIdentifier argId : argIdentifiers)
|
||||
methodArg = argId.apply(methodArg);
|
||||
|
||||
int ref = -1;
|
||||
// Find reference to argument in callArgs
|
||||
for (int ci = 0; ci < callArgsLen; ci++)
|
||||
{
|
||||
if (methodArg.tag != null && methodArg.tag.equals(params[ci].tag))
|
||||
{
|
||||
ref = ci;
|
||||
}
|
||||
else if (methodArg.type == params[ci].type)
|
||||
{
|
||||
ref = ci;
|
||||
}
|
||||
}
|
||||
if (ref < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return getArgMapping(method, false, params) != null;
|
||||
}
|
||||
|
||||
public void appendDescription(StringBuilder str)
|
||||
|
@ -97,8 +68,8 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
str.append(',');
|
||||
}
|
||||
str.append(' ');
|
||||
str.append(arg.type.getName());
|
||||
if (arg.type.isArray())
|
||||
str.append(arg.getName());
|
||||
if (arg.isArray())
|
||||
{
|
||||
str.append("[]");
|
||||
}
|
||||
|
@ -107,8 +78,7 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
str.append(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiFunction<Object, Object[], Object> getInvoker(Method method, Arg... callArgs)
|
||||
private int[] getArgMapping(Method method, boolean throwOnFailure, Arg... callArgs)
|
||||
{
|
||||
int callArgsLen = callArgs.length;
|
||||
|
||||
|
@ -122,10 +92,10 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
|
||||
// ServiceLoader for argument identification plugins
|
||||
List<ArgIdentifier> argIdentifiers = DynamicArgs.lookupArgIdentifiers();
|
||||
DynamicArgs.Arg methodArgs[] = new DynamicArgs.Arg[paramTypesLength];
|
||||
Arg methodArgs[] = new Arg[paramTypesLength];
|
||||
for (int pi = 0; pi < paramTypesLength; pi++)
|
||||
{
|
||||
methodArgs[pi] = new DynamicArgs.Arg(method, pi, paramTypes[pi]);
|
||||
methodArgs[pi] = new Arg(method, pi, paramTypes[pi]);
|
||||
|
||||
// Supplement method argument identification from plugins
|
||||
for (ArgIdentifier argId : argIdentifiers)
|
||||
|
@ -140,12 +110,7 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
// Find reference to argument in callArgs
|
||||
for (int ci = 0; ci < callArgsLen; ci++)
|
||||
{
|
||||
if (methodArgs[ai].tag != null && methodArgs[ai].tag.equals(callArgs[ci].tag))
|
||||
{
|
||||
ref = ci;
|
||||
break;
|
||||
}
|
||||
else if (methodArgs[ai].index == callArgs[ci].index)
|
||||
if (methodArgs[ai].matches(callArgs[ci]))
|
||||
{
|
||||
ref = ci;
|
||||
break;
|
||||
|
@ -156,7 +121,7 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Unable to map type [");
|
||||
err.append(methodArgs[ai].type);
|
||||
err.append(methodArgs[ai].getType());
|
||||
err.append("] in method ");
|
||||
ReflectUtils.append(err, method);
|
||||
err.append(" to calling args: (");
|
||||
|
@ -170,14 +135,80 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
}
|
||||
err.append(")");
|
||||
|
||||
throw new DynamicArgsException(err.toString());
|
||||
if (throwOnFailure)
|
||||
throw new DynamicArgsException(err.toString());
|
||||
else
|
||||
{
|
||||
LOG.debug("{}", err.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
argMapping[ai] = ref;
|
||||
}
|
||||
|
||||
// Ensure that required arguments are present in the mapping
|
||||
for (int ci = 0; ci < callArgsLen; ci++)
|
||||
{
|
||||
if (callArgs[ci].isRequired())
|
||||
{
|
||||
boolean found = false;
|
||||
for (int ai = 0; ai < argMappingLength; ai++)
|
||||
{
|
||||
if (argMapping[ai] == ci)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Unable to find required type [");
|
||||
err.append(callArgs[ci].getType());
|
||||
err.append("] in method ");
|
||||
ReflectUtils.append(err, method);
|
||||
|
||||
if (throwOnFailure)
|
||||
throw new DynamicArgsException(err.toString());
|
||||
else
|
||||
{
|
||||
LOG.debug("{}", err.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return argMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiFunction<Object, Object[], Object> getInvoker(Method method, Arg... callArgs)
|
||||
{
|
||||
int argMapping[] = getArgMapping(method, true, callArgs);
|
||||
|
||||
// Return function capable of calling method
|
||||
return (obj, potentialArgs) -> {
|
||||
return new UnorderedParamsFunction(method, argMapping);
|
||||
}
|
||||
|
||||
public static class UnorderedParamsFunction
|
||||
implements BiFunction<Object, Object[], Object>
|
||||
{
|
||||
private final Method method;
|
||||
private final int paramTypesLength;
|
||||
private final int argMapping[];
|
||||
|
||||
public UnorderedParamsFunction(Method method, int argMapping[])
|
||||
{
|
||||
this.method = method;
|
||||
this.paramTypesLength = method.getParameterTypes().length;
|
||||
this.argMapping = argMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(Object obj, Object[] potentialArgs)
|
||||
{
|
||||
Object args[] = new Object[paramTypesLength];
|
||||
for (int i = 0; i < paramTypesLength; i++)
|
||||
{
|
||||
|
@ -211,6 +242,7 @@ public class UnorderedSignature implements Signature, BiPredicate<Method, Class<
|
|||
err.append("]");
|
||||
throw new DynamicArgsException(err.toString(), e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -39,12 +39,12 @@ public class SimpleContainerScope extends ContainerLifeCycle implements WebSocke
|
|||
|
||||
public SimpleContainerScope(WebSocketPolicy policy)
|
||||
{
|
||||
this(policy,new MappedByteBufferPool(),new DecoratedObjectFactory());
|
||||
this(policy, new MappedByteBufferPool(), new DecoratedObjectFactory());
|
||||
}
|
||||
|
||||
public SimpleContainerScope(WebSocketPolicy policy, ByteBufferPool bufferPool)
|
||||
{
|
||||
this(policy,bufferPool,new DecoratedObjectFactory());
|
||||
this(policy, bufferPool, new DecoratedObjectFactory());
|
||||
}
|
||||
|
||||
public SimpleContainerScope(WebSocketPolicy policy, ByteBufferPool bufferPool, DecoratedObjectFactory objectFactory)
|
||||
|
@ -58,6 +58,15 @@ public class SimpleContainerScope extends ContainerLifeCycle implements WebSocke
|
|||
threadPool.setName(name);
|
||||
threadPool.setDaemon(true);
|
||||
this.executor = threadPool;
|
||||
|
||||
try
|
||||
{
|
||||
threadPool.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -65,7 +65,7 @@ public class ReflectUtils
|
|||
this.genericIndex = index;
|
||||
if (type instanceof Class)
|
||||
{
|
||||
this.genericClass = (Class<?>)type;
|
||||
this.genericClass = (Class<?>) type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ public class ReflectUtils
|
|||
{
|
||||
if (type instanceof Class<?>)
|
||||
{
|
||||
Class<?> ctype = (Class<?>)type;
|
||||
Class<?> ctype = (Class<?>) type;
|
||||
if (ctype.isArray())
|
||||
{
|
||||
try
|
||||
|
@ -188,16 +188,29 @@ public class ReflectUtils
|
|||
}
|
||||
}
|
||||
|
||||
public static Method findMethod(Class<?> pojo, String methodName, Class<?>... params)
|
||||
{
|
||||
try
|
||||
{
|
||||
return pojo.getMethod(methodName, params);
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Method findAnnotatedMethod(Class<?> pojo, Class<? extends Annotation> anno)
|
||||
{
|
||||
Method methods[] = findAnnotatedMethods(pojo,anno);
|
||||
if(methods == null) {
|
||||
Method methods[] = findAnnotatedMethods(pojo, anno);
|
||||
if (methods == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if(methods.length > 1)
|
||||
if (methods.length > 1)
|
||||
{
|
||||
throw DuplicateAnnotationException.build(pojo,anno,methods);
|
||||
throw DuplicateAnnotationException.build(pojo, anno, methods);
|
||||
}
|
||||
|
||||
return methods[0];
|
||||
|
@ -230,17 +243,15 @@ public class ReflectUtils
|
|||
|
||||
/**
|
||||
* Given a Base (concrete) Class, find the interface specified, and return its concrete Generic class declaration.
|
||||
*
|
||||
* @param baseClass
|
||||
* the base (concrete) class to look in
|
||||
* @param ifaceClass
|
||||
* the interface of interest
|
||||
*
|
||||
* @param baseClass the base (concrete) class to look in
|
||||
* @param ifaceClass the interface of interest
|
||||
* @return the (concrete) generic class that the interface exposes
|
||||
*/
|
||||
public static Class<?> findGenericClassFor(Class<?> baseClass, Class<?> ifaceClass)
|
||||
{
|
||||
GenericRef ref = new GenericRef(baseClass,ifaceClass);
|
||||
if (resolveGenericRef(ref,baseClass))
|
||||
GenericRef ref = new GenericRef(baseClass, ifaceClass);
|
||||
if (resolveGenericRef(ref, baseClass))
|
||||
{
|
||||
// debug("Generic Found: %s",ref.genericClass);
|
||||
return ref.genericClass;
|
||||
|
@ -317,31 +328,31 @@ public class ReflectUtils
|
|||
{
|
||||
// is this a straight ref or a TypeVariable?
|
||||
// debug("Found ref (as class): %s",toShortName(type));
|
||||
ref.setGenericFromType(type,0);
|
||||
ref.setGenericFromType(type, 0);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep digging
|
||||
return resolveGenericRef(ref,type);
|
||||
return resolveGenericRef(ref, type);
|
||||
}
|
||||
}
|
||||
|
||||
if (type instanceof ParameterizedType)
|
||||
{
|
||||
ParameterizedType ptype = (ParameterizedType)type;
|
||||
ParameterizedType ptype = (ParameterizedType) type;
|
||||
Type rawType = ptype.getRawType();
|
||||
if (rawType == ref.ifaceClass)
|
||||
{
|
||||
// debug("Found ref on [%s] as ParameterizedType [%s]",toShortName(clazz),toShortName(ptype));
|
||||
// Always get the raw type parameter, let unwrap() solve for what it is
|
||||
ref.setGenericFromType(ptype.getActualTypeArguments()[0],0);
|
||||
ref.setGenericFromType(ptype.getActualTypeArguments()[0], 0);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep digging
|
||||
return resolveGenericRef(ref,rawType);
|
||||
return resolveGenericRef(ref, rawType);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -356,7 +367,7 @@ public class ReflectUtils
|
|||
|
||||
if (type instanceof Class)
|
||||
{
|
||||
Class<?> clazz = (Class<?>)type;
|
||||
Class<?> clazz = (Class<?>) type;
|
||||
// prevent spinning off into Serialization and other parts of the
|
||||
// standard tree that we could care less about
|
||||
if (clazz.getName().matches("^javax*\\..*"))
|
||||
|
@ -368,16 +379,16 @@ public class ReflectUtils
|
|||
for (Type iface : ifaces)
|
||||
{
|
||||
// debug("resolve %s interface[]: %s",toShortName(clazz),toShortName(iface));
|
||||
if (resolveGenericRef(ref,clazz,iface))
|
||||
if (resolveGenericRef(ref, clazz, iface))
|
||||
{
|
||||
if (ref.needsUnwrap())
|
||||
{
|
||||
// debug("## Unwrap class %s::%s",toShortName(clazz),toShortName(iface));
|
||||
TypeVariable<?> needVar = (TypeVariable<?>)ref.genericType;
|
||||
TypeVariable<?> needVar = (TypeVariable<?>) ref.genericType;
|
||||
// debug("needs unwrap of type var [%s] - index [%d]",toShortName(needVar),ref.genericIndex);
|
||||
|
||||
// attempt to find typeParameter on class itself
|
||||
int typeParamIdx = findTypeParameterIndex(clazz,needVar);
|
||||
int typeParamIdx = findTypeParameterIndex(clazz, needVar);
|
||||
// debug("type param index for %s[%s] is [%d]",toShortName(clazz),toShortName(needVar),typeParamIdx);
|
||||
|
||||
if (typeParamIdx >= 0)
|
||||
|
@ -387,14 +398,14 @@ public class ReflectUtils
|
|||
TypeVariable<?> params[] = clazz.getTypeParameters();
|
||||
if (params.length >= typeParamIdx)
|
||||
{
|
||||
ref.setGenericFromType(params[typeParamIdx],typeParamIdx);
|
||||
ref.setGenericFromType(params[typeParamIdx], typeParamIdx);
|
||||
}
|
||||
}
|
||||
else if (iface instanceof ParameterizedType)
|
||||
{
|
||||
// use actual args on interface
|
||||
Type arg = ((ParameterizedType)iface).getActualTypeArguments()[ref.genericIndex];
|
||||
ref.setGenericFromType(arg,ref.genericIndex);
|
||||
Type arg = ((ParameterizedType) iface).getActualTypeArguments()[ref.genericIndex];
|
||||
ref.setGenericFromType(arg, ref.genericIndex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -402,25 +413,25 @@ public class ReflectUtils
|
|||
}
|
||||
|
||||
type = clazz.getGenericSuperclass();
|
||||
return resolveGenericRef(ref,type);
|
||||
return resolveGenericRef(ref, type);
|
||||
}
|
||||
|
||||
if (type instanceof ParameterizedType)
|
||||
{
|
||||
ParameterizedType ptype = (ParameterizedType)type;
|
||||
Class<?> rawClass = (Class<?>)ptype.getRawType();
|
||||
if (resolveGenericRef(ref,rawClass))
|
||||
ParameterizedType ptype = (ParameterizedType) type;
|
||||
Class<?> rawClass = (Class<?>) ptype.getRawType();
|
||||
if (resolveGenericRef(ref, rawClass))
|
||||
{
|
||||
if (ref.needsUnwrap())
|
||||
{
|
||||
// debug("## Unwrap ParameterizedType %s::%s",toShortName(type),toShortName(rawClass));
|
||||
TypeVariable<?> needVar = (TypeVariable<?>)ref.genericType;
|
||||
TypeVariable<?> needVar = (TypeVariable<?>) ref.genericType;
|
||||
// debug("needs unwrap of type var [%s] - index [%d]",toShortName(needVar),ref.genericIndex);
|
||||
int typeParamIdx = findTypeParameterIndex(rawClass,needVar);
|
||||
int typeParamIdx = findTypeParameterIndex(rawClass, needVar);
|
||||
// debug("type paramIdx of %s::%s is index [%d]",toShortName(rawClass),toShortName(needVar),typeParamIdx);
|
||||
|
||||
Type arg = ptype.getActualTypeArguments()[typeParamIdx];
|
||||
ref.setGenericFromType(arg,typeParamIdx);
|
||||
ref.setGenericFromType(arg, typeParamIdx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -438,15 +449,15 @@ public class ReflectUtils
|
|||
|
||||
if (type instanceof Class)
|
||||
{
|
||||
String name = ((Class<?>)type).getName();
|
||||
String name = ((Class<?>) type).getName();
|
||||
return trimClassName(name);
|
||||
}
|
||||
|
||||
if (type instanceof ParameterizedType)
|
||||
{
|
||||
ParameterizedType ptype = (ParameterizedType)type;
|
||||
ParameterizedType ptype = (ParameterizedType) type;
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append(trimClassName(((Class<?>)ptype.getRawType()).getName()));
|
||||
str.append(trimClassName(((Class<?>) ptype.getRawType()).getName()));
|
||||
str.append("<");
|
||||
Type args[] = ptype.getActualTypeArguments();
|
||||
for (int i = 0; i < args.length; i++)
|
||||
|
@ -468,7 +479,7 @@ public class ReflectUtils
|
|||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
append(str,pojo,method);
|
||||
append(str, pojo, method);
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
@ -496,7 +507,7 @@ public class ReflectUtils
|
|||
|
||||
// return type
|
||||
Type retType = method.getGenericReturnType();
|
||||
appendTypeName(str,retType,false).append(' ');
|
||||
appendTypeName(str, retType, false).append(' ');
|
||||
|
||||
if (pojo != null)
|
||||
{
|
||||
|
@ -514,7 +525,7 @@ public class ReflectUtils
|
|||
for (int j = 0; j < params.length; j++)
|
||||
{
|
||||
boolean ellipses = method.isVarArgs() && (j == (params.length - 1));
|
||||
appendTypeName(str,params[j],ellipses);
|
||||
appendTypeName(str, params[j], ellipses);
|
||||
if (j < (params.length - 1))
|
||||
{
|
||||
str.append(", ");
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.websocket.common.reflect.NameArgIdentifier
|
|
@ -1 +0,0 @@
|
|||
org.eclipse.jetty.websocket.common.util.NameArgIdentifier
|
|
@ -0,0 +1,247 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.function;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession;
|
||||
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
|
||||
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
|
||||
import org.eclipse.jetty.websocket.common.test.EventTracker;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class CommonEndpointFunctionsTest
|
||||
{
|
||||
private static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||
private WebSocketContainerScope containerScope = new SimpleContainerScope(WebSocketPolicy.newServerPolicy());
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
public Session initSession(Object websocket)
|
||||
{
|
||||
return new LocalWebSocketSession(containerScope, testname, websocket);
|
||||
}
|
||||
|
||||
public static class ConnectionOnly extends EventTracker implements WebSocketConnectionListener
|
||||
{
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, String reason)
|
||||
{
|
||||
addEvent("onWebSocketClose(%d, %s)", statusCode, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(Session session)
|
||||
{
|
||||
addEvent("onWebSocketConnect(%s)", session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketError(Throwable cause)
|
||||
{
|
||||
addEvent("onWebSocketError(%s)", cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebSocketConnectionListener_OpenTextClose()
|
||||
{
|
||||
// Setup
|
||||
ConnectionOnly socket = new ConnectionOnly();
|
||||
Session session = initSession(socket);
|
||||
EndpointFunctions<Session> endpointFunctions = new CommonEndpointFunctions(socket, containerScope.getPolicy(), containerScope.getExecutor());
|
||||
|
||||
// Trigger Events
|
||||
endpointFunctions.onOpen(session);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Hello World", UTF8), true);
|
||||
endpointFunctions.onClose(new CloseInfo(StatusCode.NORMAL, "Normal"));
|
||||
|
||||
// Validate Events
|
||||
socket.assertCaptured(
|
||||
"onWebSocketConnect\\([^\\)]*\\)",
|
||||
"onWebSocketClose\\([^\\)]*\\)");
|
||||
}
|
||||
|
||||
public static class DataConnection extends ConnectionOnly implements WebSocketListener
|
||||
{
|
||||
@Override
|
||||
public void onWebSocketBinary(byte[] payload, int offset, int len)
|
||||
{
|
||||
addEvent("onWebSocketBinary(byte[%d], %d, %d)", payload.length, offset, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(String message)
|
||||
{
|
||||
addEvent("onWebSocketText(%s)", message);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebSocketListener_OpenTextClose()
|
||||
{
|
||||
// Setup
|
||||
DataConnection socket = new DataConnection();
|
||||
Session session = initSession(socket);
|
||||
EndpointFunctions<Session> endpointFunctions = new CommonEndpointFunctions(socket, containerScope.getPolicy(), containerScope.getExecutor());
|
||||
|
||||
// Trigger Events
|
||||
endpointFunctions.onOpen(session);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Hello World", UTF8), true);
|
||||
endpointFunctions.onClose(new CloseInfo(StatusCode.NORMAL, "Normal"));
|
||||
|
||||
// Validate Events
|
||||
socket.assertCaptured(
|
||||
"onWebSocketConnect\\([^\\)]*\\)",
|
||||
"onWebSocketText\\(Hello World\\)",
|
||||
"onWebSocketClose\\([^\\)]*\\)");
|
||||
}
|
||||
|
||||
@WebSocket
|
||||
public static class StreamedText extends EventTracker
|
||||
{
|
||||
public final CountDownLatch streamLatch;
|
||||
|
||||
public StreamedText(int expectedStreams)
|
||||
{
|
||||
this.streamLatch = new CountDownLatch(expectedStreams);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@OnWebSocketMessage
|
||||
public void onTextStream(Reader reader) throws IOException
|
||||
{
|
||||
assertThat("Reader", reader, notNullValue());
|
||||
|
||||
StringWriter out = new StringWriter();
|
||||
IO.copy(reader, out);
|
||||
addEvent("onTextStream(%s)", out.toString());
|
||||
streamLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedStreamedText_Single() throws InterruptedException
|
||||
{
|
||||
// Setup
|
||||
StreamedText socket = new StreamedText(1);
|
||||
Session session = initSession(socket);
|
||||
EndpointFunctions<Session> endpointFunctions = new CommonEndpointFunctions(socket, containerScope.getPolicy(), containerScope.getExecutor());
|
||||
|
||||
// Trigger Events
|
||||
endpointFunctions.onOpen(session);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Hello World", UTF8), true);
|
||||
endpointFunctions.onClose(new CloseInfo(StatusCode.NORMAL, "Normal"));
|
||||
|
||||
// Await completion (of threads)
|
||||
socket.streamLatch.await(2, TimeUnit.SECONDS);
|
||||
|
||||
// Validate Events
|
||||
socket.assertCaptured("onTextStream\\(Hello World\\)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedStreamedText_MultipleParts() throws InterruptedException
|
||||
{
|
||||
// Setup
|
||||
StreamedText socket = new StreamedText(1);
|
||||
Session session = initSession(socket);
|
||||
EndpointFunctions<Session> endpointFunctions = new CommonEndpointFunctions(socket, containerScope.getPolicy(), containerScope.getExecutor());
|
||||
|
||||
// Trigger Events
|
||||
endpointFunctions.onOpen(session);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Hel"), false);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("lo "), false);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Wor"), false);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("ld"), true);
|
||||
endpointFunctions.onClose(new CloseInfo(StatusCode.NORMAL, "Normal"));
|
||||
|
||||
// Await completion (of threads)
|
||||
socket.streamLatch.await(2, TimeUnit.SECONDS);
|
||||
|
||||
// Validate Events
|
||||
socket.assertCaptured("onTextStream\\(Hello World\\)");
|
||||
}
|
||||
|
||||
public static class PartialData extends ConnectionOnly implements WebSocketPartialListener
|
||||
{
|
||||
@Override
|
||||
public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
|
||||
{
|
||||
addEvent("onWebSocketPartialBinary(%s, %b)", BufferUtil.toDetailString(payload), fin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketPartialText(String payload, boolean fin)
|
||||
{
|
||||
addEvent("onWebSocketPartialText(%s, %b)", payload, fin);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebSocketPartialListener()
|
||||
{
|
||||
// Setup
|
||||
PartialData socket = new PartialData();
|
||||
Session session = initSession(socket);
|
||||
EndpointFunctions<Session> endpointFunctions = new CommonEndpointFunctions(socket, containerScope.getPolicy(), containerScope.getExecutor());
|
||||
|
||||
// Trigger Events
|
||||
endpointFunctions.onOpen(session);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Hel"), false);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("lo "), false);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("Wor"), false);
|
||||
endpointFunctions.onText(BufferUtil.toBuffer("ld"), true);
|
||||
endpointFunctions.onClose(new CloseInfo(StatusCode.NORMAL, "Normal"));
|
||||
|
||||
// Validate Events
|
||||
socket.assertCaptured(
|
||||
"onWebSocketConnect\\([^\\)]*\\)",
|
||||
"onWebSocketPartialText\\(Hel, false\\)",
|
||||
"onWebSocketPartialText\\(lo , false\\)",
|
||||
"onWebSocketPartialText\\(Wor, false\\)",
|
||||
"onWebSocketPartialText\\(ld, true\\)",
|
||||
"onWebSocketClose\\([^\\)]*\\)"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ 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.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.common.test.EventCapture;
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
|
@ -47,6 +48,7 @@ public class TrackingSocket extends WebSocketAdapter
|
|||
public CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
public EventQueue<String> messageQueue = new EventQueue<>();
|
||||
public EventQueue<Throwable> errorQueue = new EventQueue<>();
|
||||
public EventCapture events = new EventCapture();
|
||||
|
||||
public TrackingSocket()
|
||||
{
|
||||
|
@ -116,6 +118,7 @@ public class TrackingSocket extends WebSocketAdapter
|
|||
public void onWebSocketBinary(byte[] payload, int offset, int len)
|
||||
{
|
||||
LOG.debug("{} onWebSocketBinary(byte[{}],{},{})",id,payload.length,offset,len);
|
||||
events.add("onWebSocketBinary(byte[{}],{},{})",payload.length,offset,len);
|
||||
messageQueue.offer(MessageDebug.toDetailHint(payload,offset,len));
|
||||
dataLatch.countDown();
|
||||
}
|
||||
|
@ -124,6 +127,7 @@ public class TrackingSocket extends WebSocketAdapter
|
|||
public void onWebSocketClose(int statusCode, String reason)
|
||||
{
|
||||
LOG.debug("{} onWebSocketClose({},{})",id,statusCode,reason);
|
||||
events.add("onWebSocketClose({},{})",statusCode,reason);
|
||||
super.onWebSocketClose(statusCode,reason);
|
||||
closeCode = statusCode;
|
||||
closeMessage.append(reason);
|
||||
|
@ -133,6 +137,7 @@ public class TrackingSocket extends WebSocketAdapter
|
|||
@Override
|
||||
public void onWebSocketConnect(Session session)
|
||||
{
|
||||
events.add("onWebSocketConnect({})",session);
|
||||
super.onWebSocketConnect(session);
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
@ -141,6 +146,7 @@ public class TrackingSocket extends WebSocketAdapter
|
|||
public void onWebSocketError(Throwable cause)
|
||||
{
|
||||
LOG.debug("{} onWebSocketError",id,cause);
|
||||
events.add("onWebSocketError({})",cause);
|
||||
Assert.assertThat("Error capture",errorQueue.offer(cause),is(true));
|
||||
}
|
||||
|
||||
|
@ -148,6 +154,7 @@ public class TrackingSocket extends WebSocketAdapter
|
|||
public void onWebSocketText(String message)
|
||||
{
|
||||
LOG.debug("{} onWebSocketText({})",id,message);
|
||||
events.add("onWebSocketText({})",message);
|
||||
messageQueue.offer(message);
|
||||
dataLatch.countDown();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -25,7 +25,6 @@ import static org.junit.Assert.assertThat;
|
|||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ExactSignatureTest
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.util;
|
||||
package org.eclipse.jetty.websocket.common.reflect;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -26,11 +26,11 @@ import java.io.File;
|
|||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UnorderedSignatureTest
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
public static class SampleSignatures
|
||||
{
|
||||
public String sigEmpty()
|
||||
|
@ -95,13 +95,13 @@ public class UnorderedSignatureTest
|
|||
DynamicArgs.Builder dab = new DynamicArgs.Builder();
|
||||
dab.addSignature(new UnorderedSignature());
|
||||
|
||||
SampleSignatures ssigs = new SampleSignatures();
|
||||
Method m = findMethodByName(ssigs, "sigEmpty");
|
||||
DynamicArgs dargs = dab.build(m);
|
||||
assertThat("DynamicArgs", dargs, notNullValue());
|
||||
SampleSignatures samples = new SampleSignatures();
|
||||
Method m = findMethodByName(samples, "sigEmpty");
|
||||
DynamicArgs dynamicArgs = dab.build(m);
|
||||
assertThat("DynamicArgs", dynamicArgs, notNullValue());
|
||||
|
||||
// Test with empty potential args
|
||||
String result = (String) dargs.invoke(ssigs);
|
||||
String result = (String) dynamicArgs.invoke(samples);
|
||||
assertThat("result", result, is("sigEmpty<>"));
|
||||
}
|
||||
|
||||
|
@ -117,13 +117,13 @@ public class UnorderedSignatureTest
|
|||
DynamicArgs.Builder dab = new DynamicArgs.Builder();
|
||||
dab.addSignature(new UnorderedSignature(ARG_STR));
|
||||
|
||||
SampleSignatures ssigs = new SampleSignatures();
|
||||
Method m = findMethodByName(ssigs, "sigEmpty");
|
||||
DynamicArgs dargs = dab.build(m);
|
||||
assertThat("DynamicArgs", dargs, notNullValue());
|
||||
SampleSignatures samples = new SampleSignatures();
|
||||
Method m = findMethodByName(samples, "sigEmpty");
|
||||
DynamicArgs dynamicArgs = dab.build(m);
|
||||
assertThat("DynamicArgs", dynamicArgs, notNullValue());
|
||||
|
||||
// Test with empty potential args
|
||||
String result = (String) dargs.invoke(ssigs, "Hello");
|
||||
String result = (String) dynamicArgs.invoke(samples, "Hello");
|
||||
assertThat("result", result, is("sigEmpty<>"));
|
||||
}
|
||||
|
||||
|
@ -140,13 +140,13 @@ public class UnorderedSignatureTest
|
|||
|
||||
final Arg CALL_STR = new Arg(String.class);
|
||||
|
||||
SampleSignatures ssigs = new SampleSignatures();
|
||||
Method m = findMethodByName(ssigs, "sigStr");
|
||||
DynamicArgs dargs = dab.build(m, CALL_STR);
|
||||
assertThat("DynamicArgs", dargs, notNullValue());
|
||||
SampleSignatures samples = new SampleSignatures();
|
||||
Method m = findMethodByName(samples, "sigStr");
|
||||
DynamicArgs dynamicArgs = dab.build(m, CALL_STR);
|
||||
assertThat("DynamicArgs", dynamicArgs, notNullValue());
|
||||
|
||||
// Test with potential args
|
||||
String result = (String) dargs.invoke(ssigs, "Hello");
|
||||
String result = (String) dynamicArgs.invoke(samples, "Hello");
|
||||
assertThat("result", result, is("sigStr<Hello>"));
|
||||
}
|
||||
|
||||
|
@ -167,18 +167,18 @@ public class UnorderedSignatureTest
|
|||
|
||||
SampleSignatures ssigs = new SampleSignatures();
|
||||
Method m = findMethodByName(ssigs, "sigByteArray");
|
||||
DynamicArgs dargs = dab.build(m,CALL_BYTEARRAY, CALL_OFFSET, CALL_LENGTH);
|
||||
assertThat("DynamicArgs", dargs, notNullValue());
|
||||
DynamicArgs dynamicArgs = dab.build(m,CALL_BYTEARRAY, CALL_OFFSET, CALL_LENGTH);
|
||||
assertThat("DynamicArgs", dynamicArgs, notNullValue());
|
||||
|
||||
// Test with potential args
|
||||
byte buf[] = new byte[222];
|
||||
int offset = 3;
|
||||
int len = 44;
|
||||
String result = (String)dargs.invoke(ssigs,buf,offset,len);
|
||||
String result = (String)dynamicArgs.invoke(ssigs,buf,offset,len);
|
||||
assertThat("result", result, is("sigByteArray<[222],3,44>"));
|
||||
|
||||
// Test with empty potential args
|
||||
result = (String)dargs.invoke(ssigs,null,123,456);
|
||||
result = (String)dynamicArgs.invoke(ssigs,null,123,456);
|
||||
assertThat("result", result, is("sigByteArray<<null>,123,456>"));
|
||||
}
|
||||
}
|
|
@ -37,9 +37,17 @@ public class DummyConnection implements LogicalConnection
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(DummyConnection.class);
|
||||
private IOState iostate;
|
||||
private WebSocketPolicy policy;
|
||||
|
||||
@Deprecated
|
||||
public DummyConnection()
|
||||
{
|
||||
this(WebSocketPolicy.newServerPolicy());
|
||||
}
|
||||
|
||||
public DummyConnection(WebSocketPolicy policy)
|
||||
{
|
||||
this.policy = policy;
|
||||
this.iostate = new IOState();
|
||||
}
|
||||
|
||||
|
@ -103,7 +111,7 @@ public class DummyConnection implements LogicalConnection
|
|||
@Override
|
||||
public WebSocketPolicy getPolicy()
|
||||
{
|
||||
return null;
|
||||
return policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -144,7 +152,7 @@ public class DummyConnection implements LogicalConnection
|
|||
public void setNextIncomingFrames(IncomingFrames incoming)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("setNextIncomingFrames({})",incoming);
|
||||
LOG.debug("setNextIncomingFrames({})", incoming);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.test;
|
||||
|
||||
import static org.eclipse.jetty.websocket.common.test.RegexMatcher.matchesPattern;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public abstract class EventTracker
|
||||
{
|
||||
private EventCapture captured = new EventCapture();
|
||||
|
||||
protected void addEvent(String format, Object... args)
|
||||
{
|
||||
captured.add(format, args);
|
||||
}
|
||||
|
||||
public void assertCaptured(String... regexEvents)
|
||||
{
|
||||
Iterator<String> capturedIterator = captured.iterator();
|
||||
for (int i = 0; i < regexEvents.length; i++)
|
||||
{
|
||||
assertThat("Event [" + i + "]", capturedIterator.next(), matchesPattern(regexEvents[i]));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.test;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Factory;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
|
||||
public class RegexMatcher extends TypeSafeMatcher
|
||||
{
|
||||
private final Pattern pattern;
|
||||
|
||||
public RegexMatcher(String pattern)
|
||||
{
|
||||
this(Pattern.compile(pattern));
|
||||
}
|
||||
|
||||
public RegexMatcher(Pattern pattern)
|
||||
{
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description)
|
||||
{
|
||||
description.appendText("matches regular expression ").appendValue(pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchesSafely(Object item)
|
||||
{
|
||||
if(item == null) return false;
|
||||
return pattern.matcher(item.toString()).matches();
|
||||
}
|
||||
|
||||
@Factory
|
||||
public static RegexMatcher matchesPattern(Pattern pattern)
|
||||
{
|
||||
return new RegexMatcher(pattern);
|
||||
}
|
||||
|
||||
@Factory
|
||||
public static RegexMatcher matchesPattern(String pattern)
|
||||
{
|
||||
return new RegexMatcher(pattern);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue