From 0a6fd16b5260caaf71b3267ad8bd4559e5501212 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 8 Mar 2016 15:20:28 -0700 Subject: [PATCH] Issue #207 - Support javax.websocket version 1.1 WIP --- .../common/functions/OnByteArrayFunction.java | 27 ++-- .../functions/OnByteBufferFunction.java | 19 ++- .../common/functions/OnCloseFunction.java | 33 +++-- .../common/functions/OnErrorFunction.java | 15 +- .../common/functions/OnFrameFunction.java | 15 +- .../functions/OnInputStreamFunction.java | 17 ++- .../common/functions/OnOpenFunction.java | 14 +- .../common/functions/OnReaderFunction.java | 17 ++- .../common/functions/OnTextFunction.java | 19 ++- .../websocket/common/util/ArgIdentifier.java | 24 ++++ .../websocket/common/util/DynamicArgs.java | 105 ++++++++++++-- .../websocket/common/util/ExactSignature.java | 25 +++- .../common/util/NameArgIdentifier.java | 36 +++++ .../common/util/UnorderedSignature.java | 68 ++++++--- ....jetty.websocket.common.util.ArgIdentifier | 1 + .../common/util/UnorderedSignatureTest.java | 129 ++++++++++-------- 16 files changed, 383 insertions(+), 181 deletions(-) create mode 100644 jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ArgIdentifier.java create mode 100644 jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/NameArgIdentifier.java create mode 100644 jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.util.ArgIdentifier diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteArrayFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteArrayFunction.java index 47cd1adaf68..dbf7597bf80 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteArrayFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteArrayFunction.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -37,25 +38,25 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnByteArrayFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int BUFFER = 2; - private static final int OFFSET = 3; - private static final int LENGTH = 4; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg BUFFER = new Arg(2,byte[].class); + private static final Arg OFFSET = new Arg(3,int.class); + private static final Arg LENGTH = new Arg(4,int.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(byte[].class).indexedAs(BUFFER)); - ARGBUILDER.addSignature(new ExactSignature(byte[].class,int.class,int.class).indexedAs(BUFFER,OFFSET,LENGTH)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class).indexedAs(SESSION,BUFFER)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class,int.class,int.class).indexedAs(SESSION,BUFFER,OFFSET,LENGTH)); + ARGBUILDER.addSignature(new ExactSignature(byte[].class)); + ARGBUILDER.addSignature(new ExactSignature(byte[].class,int.class,int.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,byte[].class,int.class,int.class)); } - + public static DynamicArgs.Builder getDynamicArgsBuilder() { return ARGBUILDER; } - + public static boolean hasMatchingSignature(Method method) { return ARGBUILDER.hasMatchingSignature(method); @@ -76,21 +77,19 @@ public class OnByteArrayFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,BUFFER,OFFSET,LENGTH); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,BUFFER,OFFSET,LENGTH); } @Override public Void apply(byte[] bin) { - Object args[] = this.callable.toArgs(session,bin,0,bin.length); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,bin,0,bin.length); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteBufferFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteBufferFunction.java index 5684bbd666a..dd009530f60 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteBufferFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnByteBufferFunction.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -38,21 +39,21 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnByteBufferFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int BUFFER = 2; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg BUFFER = new Arg(2,ByteBuffer.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(ByteBuffer.class).indexedAs(BUFFER)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,ByteBuffer.class).indexedAs(SESSION,BUFFER)); + ARGBUILDER.addSignature(new ExactSignature(ByteBuffer.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,ByteBuffer.class)); } - + public static DynamicArgs.Builder getDynamicArgsBuilder() { return ARGBUILDER; } - + public static boolean hasMatchingSignature(Method method) { return ARGBUILDER.hasMatchingSignature(method); @@ -73,21 +74,19 @@ public class OnByteBufferFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,BUFFER); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,BUFFER); } @Override public Void apply(ByteBuffer bin) { - Object args[] = this.callable.toArgs(session,bin); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,bin); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnCloseFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnCloseFunction.java index 118565126e7..904dd9a598d 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnCloseFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnCloseFunction.java @@ -18,10 +18,6 @@ package org.eclipse.jetty.websocket.common.functions; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.function.Function; - import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; @@ -29,26 +25,31 @@ import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.function.Function; + /** * Jetty {@link WebSocket} {@link OnWebSocketClose} method {@link Function} */ public class OnCloseFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int STATUS_CODE = 2; - private static final int REASON = 3; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg STATUS_CODE = new Arg(2,int.class); + private static final Arg REASON = new Arg(3,String.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature().indexedAs()); - ARGBUILDER.addSignature(new ExactSignature(Session.class).indexedAs(SESSION)); - ARGBUILDER.addSignature(new ExactSignature(int.class,String.class).indexedAs(STATUS_CODE,REASON)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,int.class,String.class).indexedAs(SESSION,STATUS_CODE,REASON)); + ARGBUILDER.addSignature(new ExactSignature()); + ARGBUILDER.addSignature(new ExactSignature(Session.class)); + ARGBUILDER.addSignature(new ExactSignature(int.class,String.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,int.class,String.class)); } private final Session session; @@ -62,25 +63,23 @@ public class OnCloseFunction implements Function this.endpoint = endpoint; this.method = method; - ReflectUtils.assertIsAnnotated(method,OnWebSocketClose.class); + ReflectUtils.assertIsAnnotated(method, OnWebSocketClose.class); ReflectUtils.assertIsPublicNonStatic(method); - ReflectUtils.assertIsReturn(method,Void.TYPE); + ReflectUtils.assertIsReturn(method, Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method, SESSION, STATUS_CODE, REASON); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketClose.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,STATUS_CODE,REASON); } @Override public Void apply(CloseInfo closeinfo) { - Object args[] = this.callable.toArgs(session,closeinfo.getStatusCode(),closeinfo.getReason()); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,closeinfo.getStatusCode(),closeinfo.getReason()); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnErrorFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnErrorFunction.java index 66125d0e467..a0a73c2485d 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnErrorFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnErrorFunction.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -37,14 +38,14 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnErrorFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int CAUSE = 2; + private static final Arg SESSION = new Arg(1, Session.class); + private static final Arg CAUSE = new Arg(2, Throwable.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(Throwable.class).indexedAs(CAUSE)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,Throwable.class).indexedAs(SESSION,CAUSE)); + ARGBUILDER.addSignature(new ExactSignature(Throwable.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,Throwable.class)); } private final Session session; @@ -62,21 +63,19 @@ public class OnErrorFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,CAUSE); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketError.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,CAUSE); } @Override public Void apply(Throwable cause) { - Object args[] = this.callable.toArgs(session,cause); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,cause); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnFrameFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnFrameFunction.java index b3a76ae7649..2c70dfe70b7 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnFrameFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnFrameFunction.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -39,14 +40,14 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnFrameFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int FRAME = 2; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg FRAME = new Arg(2,Frame.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(Frame.class).indexedAs(FRAME)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,Frame.class).indexedAs(SESSION,FRAME)); + ARGBUILDER.addSignature(new ExactSignature(Frame.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,Frame.class)); } private final Session session; @@ -64,22 +65,20 @@ public class OnFrameFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,FRAME); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketFrame.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,FRAME); } @Override public Void apply(Frame frame) { WebSocketFrame copy = WebSocketFrame.copy(frame); - Object args[] = this.callable.toArgs(session,copy); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,copy); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnInputStreamFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnInputStreamFunction.java index cbcac4ea38f..dbc0b8557a4 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnInputStreamFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnInputStreamFunction.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -39,21 +40,21 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnInputStreamFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int STREAM = 2; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg STREAM = new Arg(2, InputStream.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(InputStream.class).indexedAs(STREAM)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,InputStream.class).indexedAs(SESSION,STREAM)); + ARGBUILDER.addSignature(new ExactSignature(InputStream.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,InputStream.class)); } public static DynamicArgs.Builder getDynamicArgsBuilder() { return ARGBUILDER; } - + public static boolean hasMatchingSignature(Method method) { return ARGBUILDER.hasMatchingSignature(method); @@ -74,21 +75,19 @@ public class OnInputStreamFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,STREAM); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,STREAM); } @Override public Void apply(InputStream stream) { - Object args[] = this.callable.toArgs(session,stream); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,stream); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnOpenFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnOpenFunction.java index e52d864b491..8280f7caa3d 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnOpenFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnOpenFunction.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -37,13 +38,13 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnOpenFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; + private static final Arg SESSION = new Arg(1,Session.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature().indexedAs()); - ARGBUILDER.addSignature(new ExactSignature(Session.class).indexedAs(SESSION)); + ARGBUILDER.addSignature(new ExactSignature()); + ARGBUILDER.addSignature(new ExactSignature(Session.class)); } private final Object endpoint; @@ -59,22 +60,19 @@ public class OnOpenFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketConnect.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION); - } @Override public Void apply(Session session) { - Object args[] = this.callable.toArgs(session); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnReaderFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnReaderFunction.java index c23b80bf9c0..44d273aec6d 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnReaderFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnReaderFunction.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -38,21 +39,21 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnReaderFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int STREAM = 2; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg STREAM = new Arg(2,Reader.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(Reader.class).indexedAs(STREAM)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,Reader.class).indexedAs(SESSION,STREAM)); + ARGBUILDER.addSignature(new ExactSignature(Reader.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,Reader.class)); } public static DynamicArgs.Builder getDynamicArgsBuilder() { return ARGBUILDER; } - + public static boolean hasMatchingSignature(Method method) { return ARGBUILDER.hasMatchingSignature(method); @@ -73,21 +74,19 @@ public class OnReaderFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,STREAM); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,STREAM); } @Override public Void apply(Reader stream) { - Object args[] = this.callable.toArgs(session,stream); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,stream); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnTextFunction.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnTextFunction.java index 15ced873f22..4693e46bc8b 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnTextFunction.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/functions/OnTextFunction.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.util.DynamicArgs; +import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.ExactSignature; import org.eclipse.jetty.websocket.common.util.ReflectUtils; @@ -37,21 +38,21 @@ import org.eclipse.jetty.websocket.common.util.ReflectUtils; public class OnTextFunction implements Function { private static final DynamicArgs.Builder ARGBUILDER; - private static final int SESSION = 1; - private static final int TEXT = 2; + private static final Arg SESSION = new Arg(1,Session.class); + private static final Arg TEXT = new Arg(2,String.class); static { ARGBUILDER = new DynamicArgs.Builder(); - ARGBUILDER.addSignature(new ExactSignature(String.class).indexedAs(TEXT)); - ARGBUILDER.addSignature(new ExactSignature(Session.class,String.class).indexedAs(SESSION,TEXT)); + ARGBUILDER.addSignature(new ExactSignature(String.class)); + ARGBUILDER.addSignature(new ExactSignature(Session.class,String.class)); } - + public static DynamicArgs.Builder getDynamicArgsBuilder() { return ARGBUILDER; } - + public static boolean hasMatchingSignature(Method method) { return ARGBUILDER.hasMatchingSignature(method); @@ -72,21 +73,19 @@ public class OnTextFunction implements Function ReflectUtils.assertIsPublicNonStatic(method); ReflectUtils.assertIsReturn(method,Void.TYPE); - this.callable = ARGBUILDER.build(method); + this.callable = ARGBUILDER.build(method,SESSION,TEXT); if (this.callable == null) { throw InvalidSignatureException.build(method,OnWebSocketMessage.class,ARGBUILDER); } - this.callable.setArgReferences(SESSION,TEXT); } @Override public Void apply(String text) { - Object args[] = this.callable.toArgs(session,text); try { - method.invoke(endpoint,args); + this.callable.invoke(endpoint,session,text); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ArgIdentifier.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ArgIdentifier.java new file mode 100644 index 00000000000..1a9d9ebfae0 --- /dev/null +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ArgIdentifier.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// 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.util; + +import java.util.function.Function; + +public interface ArgIdentifier extends Function { +} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/DynamicArgs.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/DynamicArgs.java index 9cbf4443fce..a08a6eb1532 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/DynamicArgs.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/DynamicArgs.java @@ -18,12 +18,16 @@ package org.eclipse.jetty.websocket.common.util; +import org.eclipse.jetty.util.annotation.Name; + +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.Predicate; +import java.util.function.BiPredicate; /** * Provide argument utilities for working with methods that @@ -41,16 +45,16 @@ public class DynamicArgs { /** * Predicate to test if signature matches - * + * * @return the predicate to test if signature matches */ - public Predicate[]> getPredicate(); + public BiPredicate[]> getPredicate(); /** * 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 @@ -65,21 +69,59 @@ public class DynamicArgs 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 int index; - public Class type; - public Object tag; - + + 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.getSimpleName(),index,tag == null ? "" : "/" + tag); } + + public T getAnnotation(Class 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 @@ -88,10 +130,12 @@ public class DynamicArgs public DynamicArgs build(Method method, Arg... callArgs) { + // FIXME: add DynamicArgs build cache (key = method+callargs) + Class paramTypes[] = method.getParameterTypes(); for (Signature sig : signatures) { - if (sig.getPredicate().test(paramTypes)) + if (sig.getPredicate().test(method, paramTypes)) { return new DynamicArgs(sig.getInvoker(method,callArgs)); } @@ -100,6 +144,28 @@ public class DynamicArgs return null; } + /** + * 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) + { + // FIXME: add match cache (key = method) + + Class paramTypes[] = method.getParameterTypes(); + for (Signature sig : signatures) + { + if (sig.getPredicate().test(method, paramTypes)) + { + return true; + } + } + + return false; + } + public Builder addSignature(Signature sig) { signatures.add(sig); @@ -116,6 +182,23 @@ public class DynamicArgs } } + private static List argIdentifiers; + + public static List lookupArgIdentifiers() + { + if (argIdentifiers == null) + { + ServiceLoader loader = ServiceLoader.load(ArgIdentifier.class); + argIdentifiers = new ArrayList<>(); + for (ArgIdentifier argId : loader) + { + argIdentifiers.add(argId); + } + } + + return argIdentifiers; + } + private final BiFunction invoker; private DynamicArgs(BiFunction invoker) @@ -125,7 +208,7 @@ public class DynamicArgs /** * Invoke the signature / method with the provided potential args. - * + * * @param o * the object to call method on * @param potentialArgs diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ExactSignature.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ExactSignature.java index 1f6375d7b75..bbb85ec6e0c 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ExactSignature.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ExactSignature.java @@ -21,28 +21,45 @@ package org.eclipse.jetty.websocket.common.util; 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; -public class ExactSignature implements Signature, Predicate[]> +public class ExactSignature implements Signature, BiPredicate[]> { private final Arg[] params; + public ExactSignature() + { + this.params = new Arg[0]; + } + public ExactSignature(Arg... params) { this.params = params; } + public ExactSignature(Class... parameters) + { + int len = parameters.length; + + this.params = new Arg[len]; + for(int i=0; i[]> getPredicate() + public BiPredicate[]> getPredicate() { return this; } @Override - public boolean test(Class[] types) + public boolean test(Method method, Class[] types) { if (types.length != params.length) return false; @@ -158,4 +175,4 @@ public class ExactSignature implements Signature, Predicate[]> } }; } -} \ No newline at end of file +} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/NameArgIdentifier.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/NameArgIdentifier.java new file mode 100644 index 00000000000..02222c58205 --- /dev/null +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/NameArgIdentifier.java @@ -0,0 +1,36 @@ +// +// ======================================================================== +// 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.util; + +import org.eclipse.jetty.util.annotation.Name; + +/** + * Arg Identifier based on jetty's {@link org.eclipse.jetty.util.annotation.Name} annotation. + */ +public class NameArgIdentifier implements ArgIdentifier +{ + @Override + public DynamicArgs.Arg apply(DynamicArgs.Arg arg) + { + Name name = arg.getAnnotation(Name.class); + if (name != null) + arg.tag = name.value(); + return arg; + } +} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/UnorderedSignature.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/UnorderedSignature.java index 85f634180ca..a6ea84fb323 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/UnorderedSignature.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/UnorderedSignature.java @@ -22,13 +22,15 @@ 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; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Signature; -public class UnorderedSignature implements Signature, Predicate[]> +public class UnorderedSignature implements Signature, BiPredicate[]> { private final Arg[] params; @@ -36,31 +38,56 @@ public class UnorderedSignature implements Signature, Predicate[]> { this.params = args; } - + @Override - public Predicate[]> getPredicate() + public BiPredicate[]> getPredicate() { return this; } - + @Override - public boolean test(Class[] types) + public boolean test(Method method, Class[] types) { // Matches if the provided types // match the valid params in any order - int len = types.length; - for (int i = 0; i < len; i++) + // 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 argIdentifiers = DynamicArgs.lookupArgIdentifiers(); + + for (int mi = 0; mi < paramTypesLength; mi++) { - UnorderedSignature.Param p = findParam(types[i]); - if (p == null) + 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; } - public void appendDescription(StringBuilder str) { @@ -82,7 +109,7 @@ public class UnorderedSignature implements Signature, Predicate[]> } str.append(')'); } - + @Override public BiFunction getInvoker(Method method, Arg... callArgs) { @@ -94,17 +121,24 @@ public class UnorderedSignature implements Signature, Predicate[]> int argMapping[] = new int[paramTypesLength]; int callArgsLen = callArgs.length; + List argIdentifiers = DynamicArgs.lookupArgIdentifiers(); + for (int mi = 0; mi < paramTypesLength; mi++) { - // TODO: ask optional ArgFunction to populate method.paramTypes[i] Arg - // TODO: perhaps have this be loaded via ServiceLoader - // TODO: jsr356 impl can find @PathParam and populate the Arg.tag entry - + 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 (callArgs[ci].index == params[mi].index) + if (methodArg.tag != null && methodArg.tag.equals(callArgs[ci].tag)) + { + ref = ci; + } + else if (methodArg.index == callArgs[ci].index) { ref = ci; } @@ -169,4 +203,4 @@ public class UnorderedSignature implements Signature, Predicate[]> } }; } -} \ No newline at end of file +} diff --git a/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.util.ArgIdentifier b/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.util.ArgIdentifier new file mode 100644 index 00000000000..42443efb04f --- /dev/null +++ b/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.common.util.ArgIdentifier @@ -0,0 +1 @@ +org.eclipse.jetty.websocket.common.util.NameArgIdentifier diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/UnorderedSignatureTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/UnorderedSignatureTest.java index 0e8bf977d7c..83e2e5ad6ff 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/UnorderedSignatureTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/UnorderedSignatureTest.java @@ -25,6 +25,8 @@ import static org.junit.Assert.assertThat; 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 @@ -40,23 +42,23 @@ public class UnorderedSignatureTest { return String.format("sigStr<%s>",str); } - + public String sigStrFile(String str, File foo) { return String.format("sigStrFile<%s,%s>",str,foo); } - + public String sigFileStr(File foo, String str) { return String.format("sigFileStr<%s,%s>",foo,str); } - - public String sigFileStrFin(File foo, String str, boolean fin) + + public String sigFileStrFin(File foo, String str, @Name("fin") boolean fin) { return String.format("sigFileStrFin<%s,%s,%b>",foo,str,fin); } - - public String sigByteArray(byte[] buf, int offset, int len) + + public String sigByteArray(byte[] buf, @Name("offset")int offset, @Name("length")int len) { return String.format("sigByteArray<%s,%d,%d>",buf == null ? "" : ("[" + buf.length + "]"),offset,len); } @@ -73,93 +75,108 @@ public class UnorderedSignatureTest } throw new AssertionError("Unable to find method: " + name); } - - private static final int ROLE_STR = 1; - private static final int ROLE_BOOL = 2; - private static final int ROLE_FILE = 3; - private static final int ROLE_BYTEARRAY = 4; - private static final int ROLE_OFFSET = 5; - private static final int ROLE_LEN = 6; - private static final int ROLE_FIN = 7; + private static final Arg ARG_STR = new Arg(String.class); + private static final Arg ARG_BOOL = new Arg(Boolean.class); + private static final Arg ARG_FILE = new Arg(File.class); + private static final Arg ARG_BYTEARRAY = new Arg(byte[].class); + private static final Arg ARG_OFFSET = new Arg(int.class).setTag("offset"); + private static final Arg ARG_LENGTH = new Arg(int.class).setTag("length"); + private static final Arg ARG_FIN = new Arg(Boolean.class).setTag("fin"); + + /** + * 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(new UnorderedSignature() - .addParam(String.class,ROLE_STR) - .addParam(File.class,ROLE_FILE) - .addParam(Boolean.class,ROLE_FIN)); + 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()); - dargs.setArgReferences(ROLE_STR,ROLE_BOOL,ROLE_FILE); - - // Test with potential args - Object args[] = dargs.toArgs("Hello", Boolean.TRUE, new File("bar")); - - String result = (String)m.invoke(ssigs,args); - assertThat("result", result, is("sigEmpty<>")); - + // Test with empty potential args - args = dargs.toArgs(); - - result = (String)m.invoke(ssigs,args); + String result = (String) dargs.invoke(ssigs); 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 + { + 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()); + + // Test with empty potential args + String result = (String) dargs.invoke(ssigs, "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 { DynamicArgs.Builder dab = new DynamicArgs.Builder(); - dab.addSignature(new UnorderedSignature() - .addParam(String.class,ROLE_STR) - .addParam(File.class,ROLE_FILE) - .addParam(Boolean.class,ROLE_FIN)); + dab.addSignature(new UnorderedSignature(ARG_STR)); + + final Arg CALL_STR = new Arg(String.class); SampleSignatures ssigs = new SampleSignatures(); Method m = findMethodByName(ssigs, "sigStr"); - DynamicArgs dargs = dab.build(m); + DynamicArgs dargs = dab.build(m, CALL_STR); assertThat("DynamicArgs", dargs, notNullValue()); - dargs.setArgReferences(ROLE_STR,ROLE_BOOL,ROLE_FILE); - + // Test with potential args - Object args[] = dargs.toArgs("Hello", Boolean.TRUE, new File("bar")); - - String result = (String)m.invoke(ssigs,args); + String result = (String) dargs.invoke(ssigs, "Hello"); assertThat("result", result, is("sigStr")); - - // Test with empty potential args - args = dargs.toArgs(); - - result = (String)m.invoke(ssigs,args); - assertThat("result", result, is("sigStr")); } - + + /** + * Test of finding a match on a method that is tagged + * via a the ArgIdentifier concepts. + * @throws Exception on error + */ @Test public void testByteArraySignature() throws Exception { DynamicArgs.Builder dab = new DynamicArgs.Builder(); - dab.addSignature(new UnorderedSignature() - .addParam(String.class,ROLE_STR) - .addParam(File.class,ROLE_FILE) - .addParam(Boolean.class,ROLE_FIN)); + dab.addSignature(new UnorderedSignature(ARG_BYTEARRAY, ARG_OFFSET, ARG_LENGTH)); + + final Arg CALL_BYTEARRAY = new Arg(byte[].class); + final Arg CALL_OFFSET = new Arg(int.class).setTag("offset"); + final Arg CALL_LENGTH = new Arg(int.class).setTag("length"); SampleSignatures ssigs = new SampleSignatures(); Method m = findMethodByName(ssigs, "sigByteArray"); - DynamicArgs dargs = dab.build(m); + DynamicArgs dargs = dab.build(m,CALL_BYTEARRAY, CALL_OFFSET, CALL_LENGTH); assertThat("DynamicArgs", dargs, notNullValue()); - dargs.setArgReferences(ROLE_BYTEARRAY,ROLE_OFFSET,ROLE_LEN); - + // Test with potential args byte buf[] = new byte[222]; int offset = 3; int len = 44; String result = (String)dargs.invoke(m,ssigs,buf,offset,len); assertThat("result", result, is("sigByteArray<[222],3,44>")); - + // Test with empty potential args result = (String)dargs.invoke(m,ssigs,null,123,456); assertThat("result", result, is("sigByteArray<,123,456>"));