Issue #207 - Cleaning up Args vs Signature classes

This commit is contained in:
Joakim Erdfelt 2017-03-07 14:33:12 -07:00
parent daf79bf4a9
commit f6b4b7b3e3
9 changed files with 146 additions and 705 deletions

View File

@ -58,7 +58,6 @@ 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.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.JsrPongMessage;
@ -78,12 +77,12 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
{
private static final Logger LOG = Log.getLogger(JsrEndpointFunctions.class);
protected static class MessageHandlerPongFunction implements Function<ByteBuffer, Void>
private static class MessageHandlerPongFunction implements Function<ByteBuffer, Void>
{
public final MessageHandler messageHandler;
final MessageHandler messageHandler;
public final Function<ByteBuffer, Void> function;
public MessageHandlerPongFunction(MessageHandler messageHandler, Function<ByteBuffer, Void> function)
MessageHandlerPongFunction(MessageHandler messageHandler, Function<ByteBuffer, Void> function)
{
this.messageHandler = messageHandler;
this.function = function;
@ -96,12 +95,12 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
}
}
protected static class MessageHandlerSink implements MessageSink
private static class MessageHandlerSink implements MessageSink
{
public final MessageHandler messageHandler;
public final MessageSink delegateSink;
final MessageHandler messageHandler;
final MessageSink delegateSink;
public MessageHandlerSink(MessageHandler messageHandler, MessageSink messageSink)
MessageHandlerSink(MessageHandler messageHandler, MessageSink messageSink)
{
this.messageHandler = messageHandler;
this.delegateSink = messageSink;
@ -140,6 +139,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
}
}
@SuppressWarnings("unused")
public AvailableDecoders getAvailableDecoders()
{
return decoders;
@ -196,6 +196,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
{
PartialTextMessageSink sink = new PartialTextMessageSink((partial) ->
{
//noinspection unchecked
handler.onMessage((T) partial.getPayload(), partial.isFin());
return null;
});
@ -207,6 +208,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
{
PartialBinaryMessageSink sink = new PartialBinaryMessageSink((partial) ->
{
//noinspection unchecked
handler.onMessage((T) partial.getPayload(), partial.isFin());
return null;
});
@ -254,6 +256,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
{
Function<ByteBuffer, Void> pongFunction = (payload) ->
{
//noinspection unchecked
handler.onMessage((T) new JsrPongMessage(payload));
return null;
};
@ -271,6 +274,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
policy, this, decoderInstance,
(msg) ->
{
//noinspection unchecked
handler.onMessage((T) msg);
return null;
}
@ -286,6 +290,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
policy, this, decoderInstance,
(msg) ->
{
//noinspection unchecked
handler.onMessage((T) msg);
return null;
}
@ -301,6 +306,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
this, decoderInstance,
(msg) ->
{
//noinspection unchecked
handler.onMessage((T) msg);
return null;
}
@ -316,6 +322,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
this, decoderInstance,
(msg) ->
{
//noinspection unchecked
handler.onMessage((T) msg);
return null;
}
@ -425,7 +432,8 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
protected void discoverJsrAnnotatedEndpointFunctions(Object endpoint) throws DecodeException
{
Class<?> endpointClass = endpoint.getClass();
Method method = null;
Method method;
Arg SESSION = new Arg(Session.class);
// OnOpen [0..1]
method = ReflectUtils.findAnnotatedMethod(endpointClass, OnOpen.class);
@ -433,24 +441,29 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
{
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method, Void.TYPE);
Arg ENDPOINT_CONFIG = new Arg(EndpointConfig.class);
// Analyze @OnOpen method declaration techniques
DynamicArgs.Builder builder = createDynamicArgs(
new Arg(Session.class),
new Arg(EndpointConfig.class));
UnorderedSignature sigOpen = new UnorderedSignature(
createCallArgs(SESSION, ENDPOINT_CONFIG));
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) ->
int argMapping[] = sigOpen.getArgMapping(method, true);
if(argMapping != null)
{
args[0] = jsrSession;
args[1] = endpointConfig;
invoker.invoke(endpoint, args);
return null;
}, method);
assertSignatureValid(sigOpen, OnOpen.class, method);
final Object[] args = newFunctionArgs(sigOpen.getCallArgs(), method, argMapping);
BiFunction<Object, Object[], Object> invoker = sigOpen.newFunction(method);
setOnOpen((jsrSession) ->
{
args[0] = jsrSession;
args[1] = endpointConfig;
invoker.apply(endpoint, args);
return null;
}, method);
}
}
// OnClose [0..1]
@ -460,26 +473,31 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method, Void.TYPE);
Arg CLOSE_REASON = new Arg(CloseReason.class);
// Analyze @OnClose method declaration techniques
DynamicArgs.Builder builder = createDynamicArgs(
new Arg(Session.class),
new Arg(CloseReason.class));
UnorderedSignature sigClose = new UnorderedSignature(
createCallArgs(SESSION, CLOSE_REASON));
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) ->
int argMapping[] = sigClose.getArgMapping(method, true);
if(argMapping != null)
{
// 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);
assertSignatureValid(sigClose, OnClose.class, method);
final Object[] args = newFunctionArgs(sigClose.getCallArgs(), method, argMapping);
BiFunction<Object, Object[], Object> invoker = sigClose.newFunction(method);
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.apply(endpoint, args);
return null;
}, method);
}
}
// OnError [0..1]
@ -489,47 +507,50 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
ReflectUtils.assertIsPublicNonStatic(method);
ReflectUtils.assertIsReturn(method, Void.TYPE);
Arg CAUSE = new Arg(Throwable.class);
// Analyze @OnError method declaration techniques
DynamicArgs.Builder builder = createDynamicArgs(
new Arg(Session.class),
new Arg(Throwable.class));
UnorderedSignature sigError = new UnorderedSignature(
createCallArgs(SESSION, CAUSE));
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) ->
int argMapping[] = sigError.getArgMapping(method, true);
if(argMapping != null)
{
args[0] = getSession();
args[1] = cause;
invoker.invoke(endpoint, args);
return null;
}, method);
assertSignatureValid(sigError, OnError.class, method);
final Object[] args = newFunctionArgs(sigError.getCallArgs(), method, argMapping);
BiFunction<Object, Object[], Object> invoker = sigError.newFunction(method);
setOnError((cause) ->
{
args[0] = getSession();
args[1] = cause;
invoker.apply(endpoint, args);
return null;
}, method);
}
}
// OnMessage [0..3] (TEXT / BINARY / PONG)
Method onMessages[] = ReflectUtils.findAnnotatedMethods(endpointClass, OnMessage.class);
if (onMessages != null && onMessages.length > 0)
{
onmsgloop:
for (Method onMsg : onMessages)
{
// Whole TEXT / Binary Message
if (discoverOnMessageWholeText(onMsg)) continue onmsgloop;
if (discoverOnMessageWholeBinary(onMsg)) continue onmsgloop;
if (discoverOnMessageWholeText(onMsg)) continue;
if (discoverOnMessageWholeBinary(onMsg)) continue;
// Partial TEXT / BINARY
if (discoverOnMessagePartialText(onMsg)) continue onmsgloop;
if (discoverOnMessagePartialBinaryArray(onMsg)) continue onmsgloop;
if (discoverOnMessagePartialBinaryBuffer(onMsg)) continue onmsgloop;
if (discoverOnMessagePartialText(onMsg)) continue;
if (discoverOnMessagePartialBinaryArray(onMsg)) continue;
if (discoverOnMessagePartialBinaryBuffer(onMsg)) continue;
// Streaming TEXT / BINARY
if (discoverOnMessageTextStream(onMsg)) continue onmsgloop;
if (discoverOnMessageBinaryStream(onMsg)) continue onmsgloop;
if (discoverOnMessageTextStream(onMsg)) continue;
if (discoverOnMessageBinaryStream(onMsg)) continue;
// PONG
if (discoverOnMessagePong(onMsg)) continue onmsgloop;
if (discoverOnMessagePong(onMsg)) continue;
// If we reached this point, then we have a @OnMessage annotated method
// that doesn't match any known signature above.
@ -851,7 +872,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
return false;
}
private void assertSignatureValid(DynamicArgs.Signature sig, Class<? extends Annotation> annotationClass, Method method)
private void assertSignatureValid(UnorderedSignature sig, Class<? extends Annotation> annotationClass, Method method)
{
if (sig != null)
return;
@ -902,20 +923,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
}
}
@Deprecated
protected Object[] newCallArgs(Arg[] callArgs) throws DecodeException
{
int len = callArgs.length;
Object[] args = new Object[callArgs.length];
for (int i = 0; i < len; i++)
{
String staticRawValue = staticArgs.get(callArgs[i].getTag());
args[i] = AvailableDecoders.decodePrimitive(staticRawValue, callArgs[i].getType());
}
return args;
}
protected Object[] newFunctionArgs(Arg[] callArgs, Method destMethod, int argMapping[]) throws DecodeException
private Object[] newFunctionArgs(Arg[] callArgs, Method destMethod, int argMapping[]) throws DecodeException
{
Object[] potentialArgs = new Object[callArgs.length];
@ -939,21 +947,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
return potentialArgs;
}
private Object getDecodedStaticValue(String name, Class<?> type) throws DecodeException
{
String value = staticArgs.get(name);
return AvailableDecoders.decodePrimitive(value, type);
}
private DynamicArgs.Builder createDynamicArgs(Arg... args)
{
DynamicArgs.Builder argBuilder = new DynamicArgs.Builder();
Arg[] callArgs = createCallArgs(args);
argBuilder.addSignature(callArgs);
return argBuilder;
}
protected Arg[] createCallArgs(Arg... args)
private Arg[] createCallArgs(Arg... args)
{
int argCount = args.length;
if (this.staticArgs != null)

View File

@ -24,6 +24,11 @@ import org.eclipse.jetty.websocket.api.WebSocketException;
public class FunctionCallException extends WebSocketException
{
public FunctionCallException(String message)
{
super(message);
}
public FunctionCallException(String message, Throwable cause)
{
super(message, cause);

View File

@ -64,7 +64,6 @@ 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.MessageSink;
import org.eclipse.jetty.websocket.common.reflect.DynamicArgsException;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
@ -288,12 +287,8 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
{
if (t instanceof FunctionCallException)
{
return ((FunctionCallException) t).getInvokedCause();
}
else if (t instanceof DynamicArgsException)
{
Throwable cause = ((DynamicArgsException) t).getInvokedCause();
if (cause != null)
Throwable cause = ((FunctionCallException) t).getInvokedCause();
if(cause != null)
return cause;
}

View File

@ -241,7 +241,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
this.batchMode = websocket.batchMode();
Method onmethod = null;
Method onmethod;
// OnWebSocketConnect [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketConnect.class);
@ -524,7 +524,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
return onPongFunction;
}
public void setOnOpen(Function<T, Void> function, Object origin)
protected void setOnOpen(Function<T, Void> function, Object origin)
{
assertNotSet(this.onOpenFunction, "Open Handler", origin);
this.onOpenFunction = function;
@ -534,7 +534,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnClose(Function<CloseInfo, Void> function, Object origin)
protected void setOnClose(Function<CloseInfo, Void> function, Object origin)
{
assertNotSet(this.onCloseFunction, "Close Handler", origin);
this.onCloseFunction = function;
@ -544,7 +544,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnError(Function<Throwable, Void> function, Object origin)
protected void setOnError(Function<Throwable, Void> function, Object origin)
{
assertNotSet(this.onErrorFunction, "Error Handler", origin);
this.onErrorFunction = function;
@ -554,7 +554,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnText(MessageSink messageSink, Object origin)
protected void setOnText(MessageSink messageSink, Object origin)
{
assertNotSet(this.onTextSink, "TEXT Handler", origin);
this.onTextSink = messageSink;
@ -564,7 +564,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnBinary(MessageSink messageSink, Object origin)
protected void setOnBinary(MessageSink messageSink, Object origin)
{
assertNotSet(this.onBinarySink, "BINARY Handler", origin);
this.onBinarySink = messageSink;
@ -574,7 +574,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnFrame(Function<Frame, Void> function, Object origin)
protected void setOnFrame(Function<Frame, Void> function, Object origin)
{
assertNotSet(this.onFrameFunction, "Frame Handler", origin);
this.onFrameFunction = function;
@ -584,7 +584,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnPing(Function<ByteBuffer, Void> function, Object origin)
protected void setOnPing(Function<ByteBuffer, Void> function, Object origin)
{
assertNotSet(this.onPingFunction, "Ping Handler", origin);
this.onPingFunction = function;
@ -594,7 +594,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
}
}
public void setOnPong(Function<ByteBuffer, Void> function, Object origin)
protected void setOnPong(Function<ByteBuffer, Void> function, Object origin)
{
assertNotSet(this.onPongFunction, "Pong Handler", origin);
this.onPongFunction = function;
@ -644,7 +644,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
return obj.toString();
}
protected void assertNotSet(Object val, String role, Object origin)
private void assertNotSet(Object val, String role, Object origin)
{
if (val == null)
return;
@ -759,7 +759,7 @@ public class CommonEndpointFunctions<T extends Session> extends AbstractLifeCycl
onPongFunction.apply(payload);
}
protected void assertIsStarted()
private void assertIsStarted()
{
if (!isStarted())
throw new IllegalStateException(this.getClass().getName() + " not started");

View File

@ -18,31 +18,28 @@
package org.eclipse.jetty.websocket.common.reflect;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
@SuppressWarnings("serial")
public class DynamicArgsException extends RuntimeException
public class ArgIdentifiers
{
public DynamicArgsException(String message, Throwable cause)
private static List<ArgIdentifier> argIdentifiers;
public static List<ArgIdentifier> get()
{
super(message, cause);
}
public DynamicArgsException(String message)
{
super(message);
}
public Throwable getInvokedCause()
{
Throwable cause = getCause();
if (cause == null)
return null;
if (cause instanceof InvocationTargetException)
if (argIdentifiers == null)
{
return cause.getCause();
ServiceLoader<ArgIdentifier> loader = ServiceLoader.load(ArgIdentifier.class);
List<ArgIdentifier> identifiers = new ArrayList<>();
for (ArgIdentifier argId : loader)
{
identifiers.add(argId);
}
argIdentifiers = Collections.unmodifiableList(identifiers);
}
return cause;
return argIdentifiers;
}
}

View File

@ -1,30 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.reflect;
/**
* A set of potential arguments, only one of which is allowed
*/
public class ArgSwitch extends Arg
{
public ArgSwitch(Class<?> type)
{
super(type);
}
}

View File

@ -1,185 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.reflect;
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.Predicate;
/**
* Provide argument utilities for working with methods that
* have a dynamic assortment of arguments.
* <ol>
* <li>Can identify a set of parameters as matching the Builder</li>
* <li>Can create a DynamicArgs for the matched signature</li>
* <li>Can create an argument array for the provided potential arguments,
* suitable to be used with {@link Method#invoke(Object, Object...)}</li>
* </ol>
*/
public class DynamicArgs
{
public interface Signature
{
/**
* Predicate to test if signature matches
*
* @return the predicate to test if signature matches
*/
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.
* @return the return result of the invoked method
*/
BiFunction<Object, Object[], Object> getInvoker(Method method, Arg... callArgs);
void appendDescription(StringBuilder str);
}
public static class Builder implements Predicate<Method>
{
private List<Signature> signatures = new ArrayList<>();
public DynamicArgs build(Method method, Arg... callArgs)
{
Signature signature = getMatchingSignature(method);
if (signature == null)
return null;
return build(method, signature);
}
public DynamicArgs build(Method method, Signature signature)
{
return new DynamicArgs(signature.getInvoker(method, signature.getCallArgs()));
}
@Override
public boolean test(Method method)
{
return hasMatchingSignature(method);
}
/**
* Used to identify a possible method signature match.
*
* @param method the method to test
* @return true if it is a match
*/
public boolean hasMatchingSignature(Method 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)
for (Signature sig : signatures)
{
if (sig.getPredicate().test(method))
{
return sig;
}
}
return null;
}
public Builder addSignature(Arg... args)
{
signatures.add(new UnorderedSignature(args));
return this;
}
public void appendDescription(StringBuilder err)
{
for (Signature sig : signatures)
{
err.append(System.lineSeparator());
sig.appendDescription(err);
}
}
}
private static List<ArgIdentifier> argIdentifiers;
public static List<ArgIdentifier> lookupArgIdentifiers()
{
if (argIdentifiers == null)
{
ServiceLoader<ArgIdentifier> loader = ServiceLoader.load(ArgIdentifier.class);
argIdentifiers = new ArrayList<>();
for (ArgIdentifier argId : loader)
{
argIdentifiers.add(argId);
}
}
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;
public DynamicArgs(BiFunction<Object, Object[], Object> invoker)
{
this.invoker = invoker;
}
/**
* 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 Call Args
* @return the response object from the invoke
*/
public Object invoke(Object o, Object... potentialArgs)
{
return invoker.apply(o, potentialArgs);
}
}

View File

@ -22,14 +22,14 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Predicate;
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.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
public class UnorderedSignature implements Signature, Predicate<Method>
public class UnorderedSignature
{
private class SelectedArg extends Arg
{
@ -59,30 +59,31 @@ public class UnorderedSignature implements Signature, Predicate<Method>
this.callArgs = args;
}
@Override
public Arg[] getCallArgs()
{
return this.callArgs;
}
@Override
public Predicate<Method> getPredicate()
{
return this;
}
@Override
@Deprecated
public boolean test(Method method)
{
return getArgMapping(method, false, callArgs) != null;
}
public int[] getArgMapping(Method method)
{
return getArgMapping(method, false, callArgs);
}
public int[] getArgMapping(Method method, boolean throwOnFailure)
{
return getArgMapping(method, throwOnFailure, callArgs);
}
/**
* Test if the provided method is a match for this UnorderedSignature declaration
* @param method the method to test
* @return true if a match, false if not a match
*/
public boolean test(Method method)
{
return getArgMapping(method, false, callArgs) != null;
}
public void appendDescription(StringBuilder str)
{
str.append('(');
@ -116,7 +117,7 @@ public class UnorderedSignature implements Signature, Predicate<Method>
* </p>
*
* @param method the method that we want to eventually call
* @param throwOnFailure true to toss a {@link DynamicArgsException} if there is a problem
* @param throwOnFailure true to toss a {@link org.eclipse.jetty.websocket.common.FunctionCallException} if there is a problem
* attempting to identify the mapping. false to debug log the issue.
* @param callArgs the calling args for this signature
*/
@ -133,7 +134,7 @@ public class UnorderedSignature implements Signature, Predicate<Method>
int argMappingLength = argMapping.length;
// ServiceLoader for argument identification plugins
List<ArgIdentifier> argIdentifiers = DynamicArgs.lookupArgIdentifiers();
List<ArgIdentifier> argIdentifiers = ArgIdentifiers.get();
Arg methodArgs[] = new Arg[paramTypesLength];
for (int pi = 0; pi < paramTypesLength; pi++)
{
@ -187,7 +188,7 @@ public class UnorderedSignature implements Signature, Predicate<Method>
if (throwOnFailure)
{
throw new DynamicArgsException(err.toString());
throw new InvalidSignatureException(err.toString());
}
else
{
@ -214,7 +215,7 @@ public class UnorderedSignature implements Signature, Predicate<Method>
ReflectUtils.append(err, method);
if (throwOnFailure)
throw new DynamicArgsException(err.toString());
throw new FunctionCallException(err.toString());
else
{
LOG.debug("{}", err.toString());
@ -226,7 +227,6 @@ public class UnorderedSignature implements Signature, Predicate<Method>
return argMapping;
}
@Override
public BiFunction<Object, Object[], Object> getInvoker(Method method, Arg... callArgs)
{
int argMapping[] = getArgMapping(method, true, callArgs);
@ -297,7 +297,7 @@ public class UnorderedSignature implements Signature, Predicate<Method>
delim = true;
}
err.append("]");
throw new DynamicArgsException(err.toString(), e);
throw new FunctionCallException(err.toString(), e);
}
}
}

View File

@ -1,335 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.reflect;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.lang.reflect.Method;
import org.eclipse.jetty.util.annotation.Name;
import org.junit.Test;
public class DynamicArgsTest
{
public static class A
{
private final String id;
public A(String id)
{
this.id = id;
}
public String toString()
{
return String.format("A:%s",id);
}
}
public static class B
{
private final int val;
public B(int val)
{
this.val = val;
}
public String toString()
{
return String.format("B:%d",val);
}
}
@SuppressWarnings("unused")
public static class SampleSignatures
{
public String sigEmpty()
{
return "sigEmpty<>";
}
public String sigStr(String str)
{
return String.format("sigStr<%s>", q(str));
}
public String sigStrFile(String str, File foo)
{
return String.format("sigStrFile<%s,%s>", q(str), q(foo));
}
public String sigFileStr(File foo, String str)
{
return String.format("sigFileStr<%s,%s>", q(foo), q(str));
}
public String sigFileStrFin(File foo, String str, @Name("fin") boolean fin)
{
return String.format("sigFileStrFin<%s,%s,%b>", q(foo), q(str), fin);
}
public String sigByteArray(byte[] buf, @Name("offset") int offset, @Name("length") int len)
{
return String.format("sigByteArray<%s,%d,%d>", buf == null ? "<null>" : ("[" + buf.length + "]"), offset, len);
}
public String sigObjectArgs(A a, B b)
{
return String.format("sigObjectArgs<%s,%s>", q(a), q(b));
}
public String sigObjectA(A a)
{
return String.format("sigObjectA<%s>", q(a));
}
public String sigObjectB(B b)
{
return String.format("sigObjectB<%s>", q(b));
}
private String q(Object obj)
{
if (obj == null)
return "<null>";
else
return obj.toString();
}
}
public static Method findMethodByName(Object obj, String name)
{
for (Method method : obj.getClass().getMethods())
{
if (method.getName().equals(name))
{
return method;
}
}
throw new AssertionError("Unable to find method: " + name);
}
/**
* Test with method that has empty signature,
* and desired callable that also has an empty signature
*
* @throws Exception on error
*/
@Test
public void testEmptySignature() throws Exception
{
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(); // intentionally empty
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) dynamicArgs.invoke(samples);
assertThat("result", result, is("sigEmpty<>"));
}
/**
* Test with method that has empty signature,
* and desired callable that has a String (optional) signature
*
* @throws Exception on error
*/
@Test
public void testEmptySignature_StringCallable() throws Exception
{
final Arg ARG_STR = new Arg(String.class);
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(ARG_STR);
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) dynamicArgs.invoke(samples, "Hello");
assertThat("result", result, is("sigEmpty<>"));
}
/**
* Test with method that has String signature, and
* a desired callable that also has String signature.
*
* @throws Exception on error
*/
@Test
public void testStringSignature() throws Exception
{
final Arg ARG_STR = new Arg(String.class);
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(ARG_STR);
final Arg CALL_STR = new Arg(String.class);
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) dynamicArgs.invoke(samples, "Hello");
assertThat("result", result, is("sigStr<Hello>"));
}
/**
* Test of finding a match on a method that is tagged
* via the ArgIdentifier concepts.
*
* @throws Exception on error
*/
@Test
public void testByteArraySignature() throws Exception
{
final Arg ARG_BYTEARRAY = new Arg(byte[].class);
final Arg ARG_OFFSET = new Arg(int.class).setTag("offset");
final Arg ARG_LENGTH = new Arg(int.class).setTag("length");
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(ARG_BYTEARRAY, ARG_OFFSET, ARG_LENGTH);
SampleSignatures ssigs = new SampleSignatures();
Method m = findMethodByName(ssigs, "sigByteArray");
DynamicArgs dynamicArgs = dab.build(m, ARG_BYTEARRAY, ARG_OFFSET, ARG_LENGTH);
assertThat("DynamicArgs", dynamicArgs, notNullValue());
// Test with potential args
byte buf[] = new byte[222];
int offset = 3;
int len = 44;
String result = (String) dynamicArgs.invoke(ssigs, buf, offset, len);
assertThat("result", result, is("sigByteArray<[222],3,44>"));
// Test with empty potential args
result = (String) dynamicArgs.invoke(ssigs, null, 123, 456);
assertThat("result", result, is("sigByteArray<<null>,123,456>"));
}
/**
* Test of calling a method with 2 custom objects
*
* @throws Exception on error
*/
@Test
public void testObjects_A_B() throws Exception
{
final Arg ARG_A = new Arg(A.class);
final Arg ARG_B = new Arg(B.class);
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(ARG_A, ARG_B);
SampleSignatures ssigs = new SampleSignatures();
Method m = findMethodByName(ssigs, "sigObjectArgs");
DynamicArgs dynamicArgs = dab.build(m, ARG_A, ARG_B);
assertThat("DynamicArgs", dynamicArgs, notNullValue());
// Test with potential args
A a = new A("foo");
B b = new B(444);
String result = (String) dynamicArgs.invoke(ssigs, a, b);
assertThat("result", result, is("sigObjectArgs<A:foo,B:444>"));
// Test with null potential args
result = (String) dynamicArgs.invoke(ssigs, null, b);
assertThat("result", result, is("sigObjectArgs<<null>,B:444>"));
result = (String) dynamicArgs.invoke(ssigs, a, null);
assertThat("result", result, is("sigObjectArgs<A:foo,<null>>"));
}
/**
* Test of calling a method with 2 custom objects, but the method only has 1 declared
*
* @throws Exception on error
*/
@Test
public void testObjects_A() throws Exception
{
final Arg ARG_A = new Arg(A.class);
final Arg ARG_B = new Arg(B.class);
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(ARG_A, ARG_B);
SampleSignatures ssigs = new SampleSignatures();
Method m = findMethodByName(ssigs, "sigObjectA");
DynamicArgs dynamicArgs = dab.build(m, ARG_A, ARG_B);
assertThat("DynamicArgs", dynamicArgs, notNullValue());
// Test with potential args
A a = new A("foo");
B b = new B(555);
String result = (String) dynamicArgs.invoke(ssigs, a, b);
assertThat("result", result, is("sigObjectA<A:foo>"));
// Test with null potential args
result = (String) dynamicArgs.invoke(ssigs, null, b);
assertThat("result", result, is("sigObjectA<<null>>"));
result = (String) dynamicArgs.invoke(ssigs, a, null);
assertThat("result", result, is("sigObjectA<A:foo>"));
}
/**
* Test of calling a method with 2 custom objects, but the method only has 1 declared
*
* @throws Exception on error
*/
@Test
public void testObjects_B() throws Exception
{
final Arg ARG_A = new Arg(A.class);
final Arg ARG_B = new Arg(B.class);
DynamicArgs.Builder dab = new DynamicArgs.Builder();
dab.addSignature(ARG_A, ARG_B);
SampleSignatures ssigs = new SampleSignatures();
Method m = findMethodByName(ssigs, "sigObjectB");
DynamicArgs dynamicArgs = dab.build(m, ARG_A, ARG_B);
assertThat("DynamicArgs", dynamicArgs, notNullValue());
// Test with potential args
A a = new A("foo");
B b = new B(666);
String result = (String) dynamicArgs.invoke(ssigs, a, b);
assertThat("result", result, is("sigObjectB<B:666>"));
// Test with null potential args
result = (String) dynamicArgs.invoke(ssigs, null, b);
assertThat("result", result, is("sigObjectB<B:666>"));
result = (String) dynamicArgs.invoke(ssigs, a, null);
assertThat("result", result, is("sigObjectB<<null>>"));
}
}