From 0509f55cb587431a018f6ea2c1d8265d04631026 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 4 Oct 2023 12:36:53 +1100 Subject: [PATCH 1/6] add MethodHolder abstraction to avoid binding WebSocket MethodHandles Signed-off-by: Lachlan Roberts --- .../core/messages/AbstractMessageSink.java | 12 +- .../core/messages/ByteArrayMessageSink.java | 21 +- .../core/messages/ByteBufferMessageSink.java | 28 +-- .../core/messages/DispatchedMessageSink.java | 7 +- .../core/messages/InputStreamMessageSink.java | 7 +- .../messages/PartialByteArrayMessageSink.java | 11 +- .../PartialByteBufferMessageSink.java | 13 +- .../messages/PartialStringMessageSink.java | 11 +- .../core/messages/ReaderMessageSink.java | 7 +- .../core/messages/StringMessageSink.java | 11 +- .../core/util/BindingMethodHolder.java | 59 ++++++ .../core/util/BindingMethodHolder2.java | 69 ++++++ .../websocket/core/util/InvokerUtils.java | 12 +- .../util/LambdaMetafactoryMethodHolder.java | 73 +++++++ .../websocket/core/util/MethodHolder.java | 107 ++++++++++ .../core/util/NonBindingMethodHolder.java | 103 +++++++++ .../util/PartialStringMessageSinkTest.java | 2 +- .../core/util/StringMessageSinkTest.java | 14 +- .../common/JettyWebSocketFrameHandler.java | 37 ++-- .../JettyWebSocketFrameHandlerFactory.java | 1 + .../internal/ByteBufferMessageSink.java | 20 +- .../PartialByteBufferMessageSink.java | 18 +- .../common/OutgoingMessageCapture.java | 5 +- .../JakartaMessagePartialMethodHolder.java | 59 ++++++ .../JakartaMessageWholeMethodHolder.java | 57 +++++ .../common/JakartaWebSocketFrameHandler.java | 187 +++++++--------- .../JakartaWebSocketFrameHandlerFactory.java | 67 +++--- .../JakartaWebSocketMessageMetadata.java | 14 +- .../messages/AbstractDecodedMessageSink.java | 19 +- .../messages/DecodedBinaryMessageSink.java | 24 ++- .../DecodedBinaryStreamMessageSink.java | 21 +- .../messages/DecodedTextMessageSink.java | 24 ++- .../DecodedTextStreamMessageSink.java | 21 +- .../DecodedBinaryMessageSinkTest.java | 5 +- .../DecodedBinaryStreamMessageSinkTest.java | 5 +- .../messages/DecodedTextMessageSinkTest.java | 5 +- .../DecodedTextStreamMessageSinkTest.java | 5 +- .../messages/InputStreamMessageSinkTest.java | 9 +- .../messages/ReaderMessageSinkTest.java | 5 +- .../util/InvokerUtilsStaticParamsTest.java | 31 +-- .../tests/coders/DecoderTextStreamTest.java | 3 +- .../jetty/ee9/servlet/DefaultServletTest.java | 1 + .../JakartaMessagePartialMethodHolder.java | 59 ++++++ .../JakartaMessageWholeMethodHolder.java | 57 +++++ .../common/JakartaWebSocketFrameHandler.java | 191 +++++++---------- .../JakartaWebSocketFrameHandlerFactory.java | 88 +++----- .../JakartaWebSocketMessageMetadata.java | 14 +- .../messages/AbstractDecodedMessageSink.java | 20 +- .../messages/DecodedBinaryMessageSink.java | 21 +- .../DecodedBinaryStreamMessageSink.java | 24 ++- .../messages/DecodedTextMessageSink.java | 23 +- .../DecodedTextStreamMessageSink.java | 23 +- .../messages/AbstractMessageSinkTest.java | 5 +- .../DecodedBinaryMessageSinkTest.java | 5 +- .../DecodedBinaryStreamMessageSinkTest.java | 5 +- .../messages/DecodedTextMessageSinkTest.java | 5 +- .../DecodedTextStreamMessageSinkTest.java | 5 +- .../messages/InputStreamMessageSinkTest.java | 9 +- .../messages/ReaderMessageSinkTest.java | 5 +- .../util/InvokerUtilsStaticParamsTest.java | 31 +-- .../tests/coders/DecoderTextStreamTest.java | 3 +- .../common/JettyWebSocketFrameHandler.java | 35 ++- .../JettyWebSocketFrameHandlerFactory.java | 22 +- .../common/OutgoingMessageCapture.java | 5 +- tests/jetty-jmh/pom.xml | 8 + .../jetty/websocket/jmh/MetafactoryTest.java | 53 +++++ .../websocket/jmh/MethodHolderBenchmark.java | 169 +++++++++++++++ .../websocket/jmh/WebSocketBenchmark.java | 200 ++++++++++++++++++ 68 files changed, 1686 insertions(+), 609 deletions(-) create mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java create mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java create mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java create mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java create mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/NonBindingMethodHolder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java create mode 100644 jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java create mode 100644 jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java create mode 100644 tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java create mode 100644 tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java create mode 100644 tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/AbstractMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/AbstractMessageSink.java index fa6349e1f117..979098405bd8 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/AbstractMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/AbstractMessageSink.java @@ -13,10 +13,10 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; import java.util.Objects; import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

Abstract implementation of {@link MessageSink}.

@@ -42,21 +42,21 @@ public abstract class AbstractMessageSink implements MessageSink { private final CoreSession session; - private final MethodHandle methodHandle; + private final MethodHolder methodHandle; private final boolean autoDemand; /** * Creates a new {@link MessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke + * @param methodHolder the application function to invoke * @param autoDemand whether this {@link MessageSink} manages demand automatically * as explained in {@link AbstractMessageSink} */ - public AbstractMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public AbstractMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { this.session = Objects.requireNonNull(session, "CoreSession"); - this.methodHandle = Objects.requireNonNull(methodHandle, "MethodHandle"); + this.methodHandle = Objects.requireNonNull(methodHolder, "MethodHolder"); this.autoDemand = autoDemand; } @@ -73,7 +73,7 @@ public CoreSession getCoreSession() * Get the application function. * @return the application function */ - public MethodHandle getMethodHandle() + public MethodHolder getMethodHolder() { return methodHandle; } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java index e6be28115aae..be2b0a3c1314 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.MessageTooLargeException; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A {@link MessageSink} implementation that accumulates BINARY frames @@ -38,18 +39,18 @@ public class ByteArrayMessageSink extends AbstractMessageSink * Creates a new {@link ByteArrayMessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke when a new message has been assembled + * @param methodHolder the application function to invoke when a new message has been assembled * @param autoDemand whether this {@link MessageSink} manages demand automatically */ - public ByteArrayMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public ByteArrayMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); - // This uses the offset length byte array signature not supported by jakarta websocket. - // The jakarta layer instead uses decoders for whole byte array messages instead of this message sink. - MethodType onMessageType = MethodType.methodType(Void.TYPE, byte[].class, int.class, int.class); - if (methodHandle.type().changeReturnType(void.class) != onMessageType.changeReturnType(void.class)) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); + // TODO: This uses the offset length byte array signature not supported by jakarta websocket. + // The jakarta layer instead uses decoders for whole byte array messages instead of this message sink. + // MethodType onMessageType = MethodType.methodType(Void.TYPE, byte[].class, int.class, int.class); + // if (methodHolder.type().changeReturnType(void.class) != onMessageType.changeReturnType(void.class)) + // throw InvalidSignatureException.build(onMessageType, methodHolder.type()); } @Override @@ -69,7 +70,7 @@ public void accept(Frame frame, Callback callback) if (frame.isFin() && accumulator == null) { byte[] buf = BufferUtil.toArray(payload); - getMethodHandle().invoke(buf, 0, buf.length); + getMethodHolder().invoke(buf, 0, buf.length); callback.succeeded(); autoDemand(); return; @@ -91,7 +92,7 @@ public void accept(Frame frame, Callback callback) // Do not complete twice the callback if the invocation fails. callback = Callback.NOOP; byte[] buf = accumulator.takeByteArray(); - getMethodHandle().invoke(buf, 0, buf.length); + getMethodHolder().invoke(buf, 0, buf.length); autoDemand(); } else diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java index afcec2dda7ef..9c0bc1523278 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.websocket.core.messages; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.nio.ByteBuffer; import org.eclipse.jetty.io.ByteBufferCallbackAccumulator; @@ -23,8 +22,8 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; -import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.MessageTooLargeException; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A {@link MessageSink} implementation that accumulates BINARY frames @@ -39,23 +38,24 @@ public class ByteBufferMessageSink extends AbstractMessageSink * Creates a new {@link ByteBufferMessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke when a new message has been assembled + * @param methodHolder the application function to invoke when a new message has been assembled * @param autoDemand whether this {@link MessageSink} manages demand automatically */ - public ByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public ByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - this(session, methodHandle, autoDemand, true); + this(session, methodHolder, autoDemand, true); } - protected ByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand, boolean validateSignature) + protected ByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand, boolean validateSignature) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); if (validateSignature) { - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class); - if (methodHandle.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); + // TODO: fix + // MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class); + // if (methodHolder.type() != onMessageType) + // throw InvalidSignatureException.build(onMessageType, methodHandle.type()); } } @@ -74,7 +74,7 @@ public void accept(Frame frame, Callback callback) if (frame.isFin() && accumulator == null) { - invoke(getMethodHandle(), frame.getPayload(), callback); + invoke(getMethodHolder(), frame.getPayload(), callback); autoDemand(); return; } @@ -97,7 +97,7 @@ public void accept(Frame frame, Callback callback) ByteBuffer byteBuffer = buffer.getByteBuffer(); accumulator.writeTo(byteBuffer); callback = Callback.from(buffer::release); - invoke(getMethodHandle(), byteBuffer, callback); + invoke(getMethodHolder(), byteBuffer, callback); autoDemand(); } else @@ -126,9 +126,9 @@ public void fail(Throwable failure) accumulator.fail(failure); } - protected void invoke(MethodHandle methodHandle, ByteBuffer byteBuffer, Callback callback) throws Throwable + protected void invoke(MethodHolder methodHolder, ByteBuffer byteBuffer, Callback callback) throws Throwable { - methodHandle.invoke(byteBuffer); + methodHolder.invoke(byteBuffer); callback.succeeded(); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/DispatchedMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/DispatchedMessageSink.java index c15302966912..e7c36792d5b6 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/DispatchedMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/DispatchedMessageSink.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A partial implementation of {@link MessageSink} for methods that consume WebSocket @@ -49,9 +50,9 @@ public abstract class DispatchedMessageSink extends AbstractMessageSink private volatile CompletableFuture dispatchComplete; private MessageSink typeSink; - public DispatchedMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public DispatchedMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); if (!autoDemand) throw new IllegalArgumentException("%s must be auto-demanding".formatted(getClass().getSimpleName())); executor = session.getWebSocketComponents().getExecutor(); @@ -72,7 +73,7 @@ public void accept(Frame frame, final Callback callback) { try { - getMethodHandle().invoke(typeSink); + getMethodHolder().invoke(typeSink); if (typeSink instanceof Closeable closeable) IO.close(closeable); dispatchComplete.complete(null); diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/InputStreamMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/InputStreamMessageSink.java index 72c65802387f..bbf826747cbc 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/InputStreamMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/InputStreamMessageSink.java @@ -13,15 +13,14 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; - import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class InputStreamMessageSink extends DispatchedMessageSink { - public InputStreamMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public InputStreamMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteArrayMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteArrayMessageSink.java index c07b35484bcc..37da9c08c2c1 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteArrayMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteArrayMessageSink.java @@ -13,12 +13,11 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; - import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A {@link MessageSink} implementation that delivers BINARY frames @@ -31,12 +30,12 @@ public class PartialByteArrayMessageSink extends AbstractMessageSink * Creates a new {@link PartialByteArrayMessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke when a new frame has arrived + * @param methodHolder the application function to invoke when a new frame has arrived * @param autoDemand whether this {@link MessageSink} manages demand automatically */ - public PartialByteArrayMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public PartialByteArrayMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); } @Override @@ -47,7 +46,7 @@ public void accept(Frame frame, Callback callback) if (frame.hasPayload() || frame.isFin()) { byte[] buffer = BufferUtil.toArray(frame.getPayload()); - getMethodHandle().invoke(buffer, frame.isFin()); + getMethodHolder().invoke(buffer, frame.isFin()); callback.succeeded(); autoDemand(); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteBufferMessageSink.java index 5cdf0ebba02d..3ebe188d3fd8 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialByteBufferMessageSink.java @@ -19,6 +19,7 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A {@link MessageSink} implementation that delivers BINARY frames @@ -31,12 +32,12 @@ public class PartialByteBufferMessageSink extends AbstractMessageSink * Creates a new {@link PartialByteBufferMessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke when a new frame has arrived + * @param methodHolder the application function to invoke when a new frame has arrived * @param autoDemand whether this {@link MessageSink} manages demand automatically */ - public PartialByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public PartialByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); } @Override @@ -46,7 +47,7 @@ public void accept(Frame frame, Callback callback) { if (frame.hasPayload() || frame.isFin()) { - invoke(getMethodHandle(), frame.getPayload(), frame.isFin(), callback); + invoke(getMethodHolder(), frame.getPayload(), frame.isFin(), callback); autoDemand(); } else @@ -61,9 +62,9 @@ public void accept(Frame frame, Callback callback) } } - protected void invoke(MethodHandle methodHandle, ByteBuffer byteBuffer, boolean fin, Callback callback) throws Throwable + protected void invoke(MethodHolder methodHolder, ByteBuffer byteBuffer, boolean fin, Callback callback) throws Throwable { - methodHandle.invoke(byteBuffer, fin); + methodHolder.invoke(byteBuffer, fin); callback.succeeded(); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialStringMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialStringMessageSink.java index ba74c2c5f134..b3548ad83c39 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialStringMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/PartialStringMessageSink.java @@ -20,6 +20,7 @@ import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.exception.BadPayloadException; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A {@link MessageSink} implementation that delivers TEXT frames @@ -34,12 +35,12 @@ public class PartialStringMessageSink extends AbstractMessageSink * Creates a new {@link PartialStringMessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke when a new frame has arrived + * @param methodHolder the application function to invoke when a new frame has arrived * @param autoDemand whether this {@link MessageSink} manages demand automatically */ - public PartialStringMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public PartialStringMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); } @Override @@ -55,12 +56,12 @@ public void accept(Frame frame, Callback callback) if (frame.isFin()) { String complete = accumulator.takeCompleteString(BadPayloadException.InvalidUtf8::new); - getMethodHandle().invoke(complete, true); + getMethodHolder().invoke(complete, true); } else { String partial = accumulator.takePartialString(BadPayloadException.InvalidUtf8::new); - getMethodHandle().invoke(partial, false); + getMethodHolder().invoke(partial, false); } callback.succeeded(); diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ReaderMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ReaderMessageSink.java index 8f8e4f1759a1..fb6cc87f1e7f 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ReaderMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ReaderMessageSink.java @@ -13,15 +13,14 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; - import org.eclipse.jetty.websocket.core.CoreSession; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class ReaderMessageSink extends DispatchedMessageSink { - public ReaderMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public ReaderMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/StringMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/StringMessageSink.java index 5a7230acdf76..89f1e06b74cd 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/StringMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/StringMessageSink.java @@ -13,14 +13,13 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; - import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.exception.BadPayloadException; import org.eclipse.jetty.websocket.core.exception.MessageTooLargeException; +import org.eclipse.jetty.websocket.core.util.MethodHolder; /** *

A {@link MessageSink} implementation that accumulates TEXT frames @@ -36,12 +35,12 @@ public class StringMessageSink extends AbstractMessageSink * Creates a new {@link StringMessageSink}. * * @param session the WebSocket session - * @param methodHandle the application function to invoke when a new message has been assembled + * @param methodHolder the application function to invoke when a new message has been assembled * @param autoDemand whether this {@link MessageSink} manages demand automatically */ - public StringMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public StringMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); this.size = 0; } @@ -65,7 +64,7 @@ public void accept(Frame frame, Callback callback) if (frame.isFin()) { - getMethodHandle().invoke(out.takeCompleteString(BadPayloadException.InvalidUtf8::new)); + getMethodHolder().invoke(out.takeCompleteString(BadPayloadException.InvalidUtf8::new)); callback.succeeded(); autoDemand(); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java new file mode 100644 index 000000000000..5f7553764fe2 --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +class BindingMethodHolder implements MethodHolder +{ + public MethodHandle _methodHandle; + + public BindingMethodHolder(MethodHandle methodHandle) + { + _methodHandle = methodHandle; + } + + @Override + public Object invoke(Object... args) throws Throwable + { + return _methodHandle.invokeWithArguments(args); + } + + @Override + public BindingMethodHolder bindTo(Object arg) + { + _methodHandle = _methodHandle.bindTo(arg); + return this; + } + + @Override + public MethodHolder bindTo(Object arg, int idx) + { + _methodHandle = MethodHandles.insertArguments(_methodHandle, idx, arg); + return this; + } + + @Override + public Class parameterType(int idx) + { + return _methodHandle.type().parameterType(idx); + } + + @Override + public Class returnType() + { + return _methodHandle.type().returnType(); + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java new file mode 100644 index 000000000000..29eb9932859f --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +public class BindingMethodHolder2 implements MethodHolder +{ + public MethodHandle _methodHandle; + + public BindingMethodHolder2(MethodHandle methodHandle) + { + _methodHandle = methodHandle; + } + + @Override + public Object invoke(Object... args) throws Throwable + { + return MethodHolder.doInvoke(_methodHandle, args); + } + + public MethodHandle getMethodHandler() + { + return _methodHandle; + } + + public Object invoke(Object o1, Object o2) throws Throwable + { + return MethodHolder.doInvoke(_methodHandle, o1, o2); + } + + @Override + public BindingMethodHolder2 bindTo(Object arg) + { + _methodHandle = _methodHandle.bindTo(arg); + return this; + } + + @Override + public MethodHolder bindTo(Object arg, int idx) + { + _methodHandle = MethodHandles.insertArguments(_methodHandle, idx, arg); + return this; + } + + @Override + public Class parameterType(int idx) + { + return _methodHandle.type().parameterType(idx); + } + + @Override + public Class returnType() + { + return _methodHandle.type().returnType(); + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/InvokerUtils.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/InvokerUtils.java index 6fc71474e630..e8a6e6c68e81 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/InvokerUtils.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/InvokerUtils.java @@ -135,18 +135,18 @@ public Arg getParamArg(Method method, Class paramType, int idx) /** * Bind optional arguments to provided method handle * - * @param methodHandle the method handle to bind to + * @param methodHolder the method handle to bind to * @param objs the list of optional objects to bind to. - * @return the bound MethodHandle, or null if the provided {@code methodHandle} was null. + * @return the bound MethodHandle, or null if the provided {@code methodHolder} was null. */ - public static MethodHandle bindTo(MethodHandle methodHandle, Object... objs) + public static MethodHolder bindTo(MethodHolder methodHolder, Object... objs) { - if (methodHandle == null) + if (methodHolder == null) return null; - MethodHandle ret = methodHandle; + MethodHolder ret = methodHolder; for (Object obj : objs) { - if (ret.type().parameterType(0).isAssignableFrom(obj.getClass())) + if (ret.parameterType(0).isAssignableFrom(obj.getClass())) { ret = ret.bindTo(obj); } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java new file mode 100644 index 000000000000..a5cd13a75be2 --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java @@ -0,0 +1,73 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.util; + +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.function.Supplier; + +public class LambdaMetafactoryMethodHolder implements MethodHolder +{ + private final CallSite _callSite; + + public LambdaMetafactoryMethodHolder(MethodHandle methodHandle, MethodHandles.Lookup lookup) throws Throwable + { + MethodType methodType = methodHandle.type().changeReturnType(Supplier.class); + _callSite = LambdaMetafactory.metafactory( + lookup, + "get", + methodType, + MethodType.methodType(Object.class), + methodHandle, + // Supplier method real signature (reified) + // trim accepts no parameters and returns String + MethodType.methodType(methodType.returnType())); + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(Object... args) throws Throwable + { + return ((Supplier)MethodHolder.doInvoke(_callSite.getTarget(), args)).get(); + } + + @Override + public LambdaMetafactoryMethodHolder bindTo(Object arg) + { + _callSite.setTarget(_callSite.getTarget().bindTo(arg)); + return this; + } + + @Override + public MethodHolder bindTo(Object arg, int idx) + { + _callSite.setTarget(MethodHandles.insertArguments(_callSite.getTarget(), idx, arg)); + return this; + } + + @Override + public Class parameterType(int idx) + { + return _callSite.type().parameterType(idx); + } + + @Override + public Class returnType() + { + return _callSite.type().returnType(); + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java new file mode 100644 index 000000000000..8f0b8ee64692 --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java @@ -0,0 +1,107 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.util; + +import java.lang.invoke.MethodHandle; + +/** + * An interface for managing invocations of methods whose arguments may need to be augmented, by + * binding in certain parameters ahead of time. + * + * Implementations may use various invocation mechanisms, including: + *
    + *
  • direct method invocation on an held object
  • + *
  • calling a method pointer
  • + *
  • calling a MethodHandle bound to the known arguments
  • + *
  • calling a MethodHandle without binding to the known arguments
  • + *
+ * + * Implementations of this may not be thread safe, so the caller must use some external mutual exclusion + * unless they are using a specific implementation known to be thread-safe. + */ +public interface MethodHolder +{ + String METHOD_HOLDER_BINDING_PROPERTY = "jetty.websocket.methodholder.binding"; + + static MethodHolder from(MethodHandle methodHandle) + { + String property = System.getProperty(METHOD_HOLDER_BINDING_PROPERTY); + return from(methodHandle, Boolean.parseBoolean(property)); + } + + static MethodHolder from(MethodHandle methodHandle, boolean binding) + { + if (methodHandle == null) + return null; + return binding ? new BindingMethodHolder(methodHandle) : new NonBindingMethodHolder(methodHandle); + } + + Object invoke(Object... args) throws Throwable; + + default MethodHolder bindTo(Object arg) + { + throw new UnsupportedOperationException(); + } + + default MethodHolder bindTo(Object arg, int idx) + { + throw new UnsupportedOperationException(); + } + + default Class parameterType(int idx) + { + throw new UnsupportedOperationException(); + } + + default Class returnType() + { + throw new UnsupportedOperationException(); + } + + static Object doInvoke(MethodHandle methodHandle, Object arg1, Object arg2) throws Throwable + { + return methodHandle.invoke(arg1, arg2); + } + + static Object doInvoke(MethodHandle methodHandle, Object... args) throws Throwable + { + return methodHandle.invokeExact(args[0], args[1]); + +// switch (args.length) +// { +// case 0: +// return methodHandle.invoke(); +// case 1: +// return methodHandle.invoke(args[0]); +// case 2: +// return methodHandle.invoke(args[0], args[1]); +// case 3: +// return methodHandle.invoke(args[0], args[1], args[2]); +// case 4: +// return methodHandle.invoke(args[0], args[1], args[2], args[3]); +// case 5: +// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4]); +// case 6: +// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5]); +// case 7: +// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); +// case 8: +// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); +// case 9: +// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); +// default: +// return methodHandle.invokeWithArguments(args); +// } + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/NonBindingMethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/NonBindingMethodHolder.java new file mode 100644 index 000000000000..b92fa4c6672f --- /dev/null +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/NonBindingMethodHolder.java @@ -0,0 +1,103 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.core.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * This implementation of {@link MethodHolder} is not thread safe. + * Mutual exclusion should be used when calling {@link #invoke(Object...)}, or this should only + * be invoked from a single thread. + */ +class NonBindingMethodHolder implements MethodHolder +{ + private final MethodHandle _methodHandle; + private final Object[] _parameters; + private final List _unboundParamIndexes = new ArrayList<>(); + + public NonBindingMethodHolder(MethodHandle methodHandle) + { + _methodHandle = Objects.requireNonNull(methodHandle); + int numParams = methodHandle.type().parameterCount(); + _parameters = new Object[numParams]; + for (int i = 0; i < numParams; i++) + { + _unboundParamIndexes.add(i); + } + } + + @Override + public Object invoke(Object... args) throws Throwable + { + try + { + insertArguments(args); + return MethodHolder.doInvoke(_methodHandle, _parameters); + } + finally + { + clearArguments(); + } + } + + @Override + public MethodHolder bindTo(Object arg, int idx) + { + _parameters[_unboundParamIndexes.get(idx)] = arg; + _unboundParamIndexes.remove(idx); + return this; + } + + @Override + public MethodHolder bindTo(Object arg) + { + return bindTo(arg, 0); + } + + private void insertArguments(Object... args) + { + if (_unboundParamIndexes.size() != args.length) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", _unboundParamIndexes.size(), args.length)); + + int argsIndex = 0; + for (int index : _unboundParamIndexes) + { + _parameters[index] = args[argsIndex++]; + } + } + + private void clearArguments() + { + for (int i : _unboundParamIndexes) + { + _parameters[i] = null; + } + } + + @Override + public Class parameterType(int idx) + { + return _methodHandle.type().parameterType(_unboundParamIndexes.get(idx)); + } + + @Override + public Class returnType() + { + return _methodHandle.type().returnType(); + } +} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/PartialStringMessageSinkTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/PartialStringMessageSinkTest.java index 80d6cd4302dc..daa360a48227 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/PartialStringMessageSinkTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/PartialStringMessageSinkTest.java @@ -51,7 +51,7 @@ public class PartialStringMessageSinkTest @BeforeEach public void before() throws Exception { - messageSink = new PartialStringMessageSink(coreSession, endpoint.getMethodHandle(), true); + messageSink = new PartialStringMessageSink(coreSession, MethodHolder.from(endpoint.getMethodHandle()), true); } @Test diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/StringMessageSinkTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/StringMessageSinkTest.java index a5448b74b45b..69cbd26ece22 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/StringMessageSinkTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/util/StringMessageSinkTest.java @@ -38,13 +38,13 @@ public class StringMessageSinkTest { - private CoreSession coreSession = new CoreSession.Empty(); - private OnMessageEndpoint endpoint = new OnMessageEndpoint(); + private final CoreSession coreSession = new CoreSession.Empty(); + private final OnMessageEndpoint endpoint = new OnMessageEndpoint(); @Test public void testMaxMessageSize() throws Exception { - StringMessageSink messageSink = new StringMessageSink(coreSession, endpoint.getMethodHandle(), true); + StringMessageSink messageSink = new StringMessageSink(coreSession, MethodHolder.from(endpoint.getMethodHandle()), true); ByteBuffer utf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0xF0, (byte)0x90, (byte)0x8D, (byte)0x88}); FutureCallback callback = new FutureCallback(); @@ -60,7 +60,7 @@ public void testMaxMessageSize() throws Exception @Test public void testValidUtf8() throws Exception { - StringMessageSink messageSink = new StringMessageSink(coreSession, endpoint.getMethodHandle(), true); + StringMessageSink messageSink = new StringMessageSink(coreSession, MethodHolder.from(endpoint.getMethodHandle()), true); ByteBuffer utf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0xF0, (byte)0x90, (byte)0x8D, (byte)0x88}); FutureCallback callback = new FutureCallback(); @@ -73,7 +73,7 @@ public void testValidUtf8() throws Exception @Test public void testUtf8Continuation() throws Exception { - StringMessageSink messageSink = new StringMessageSink(coreSession, endpoint.getMethodHandle(), true); + StringMessageSink messageSink = new StringMessageSink(coreSession, MethodHolder.from(endpoint.getMethodHandle()), true); ByteBuffer firstUtf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0xF0, (byte)0x90}); ByteBuffer continuationUtf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0x8D, (byte)0x88}); @@ -91,7 +91,7 @@ public void testUtf8Continuation() throws Exception @Test public void testInvalidSingleFrameUtf8() throws Exception { - StringMessageSink messageSink = new StringMessageSink(coreSession, endpoint.getMethodHandle(), true); + StringMessageSink messageSink = new StringMessageSink(coreSession, MethodHolder.from(endpoint.getMethodHandle()), true); ByteBuffer invalidUtf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0xF0, (byte)0x90, (byte)0x8D}); FutureCallback callback = new FutureCallback(); @@ -106,7 +106,7 @@ public void testInvalidSingleFrameUtf8() throws Exception @Test public void testInvalidMultiFrameUtf8() throws Exception { - StringMessageSink messageSink = new StringMessageSink(coreSession, endpoint.getMethodHandle(), true); + StringMessageSink messageSink = new StringMessageSink(coreSession, MethodHolder.from(endpoint.getMethodHandle()), true); ByteBuffer firstUtf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0xF0, (byte)0x90}); ByteBuffer continuationUtf8Payload = BufferUtil.toBuffer(new byte[]{(byte)0x8D}); diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java index 5a998f9806b2..f4cce5e443e8 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java @@ -42,6 +42,7 @@ import org.eclipse.jetty.websocket.core.exception.WebSocketTimeoutException; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,16 +53,16 @@ public class JettyWebSocketFrameHandler implements FrameHandler private final WebSocketContainer container; private final Object endpointInstance; private final JettyWebSocketFrameHandlerMetadata metadata; - private MethodHandle openHandle; - private MethodHandle closeHandle; - private MethodHandle errorHandle; - private MethodHandle textHandle; - private MethodHandle binaryHandle; + private MethodHolder openHandle; + private MethodHolder closeHandle; + private MethodHolder errorHandle; + private MethodHolder textHandle; + private MethodHolder binaryHandle; private final Class textSinkClass; private final Class binarySinkClass; - private MethodHandle frameHandle; - private MethodHandle pingHandle; - private MethodHandle pongHandle; + private MethodHolder frameHandle; + private MethodHolder pingHandle; + private MethodHolder pongHandle; private UpgradeRequest upgradeRequest; private UpgradeResponse upgradeResponse; private MessageSink textSink; @@ -76,16 +77,16 @@ public JettyWebSocketFrameHandler(WebSocketContainer container, Object endpointI this.endpointInstance = endpointInstance; this.metadata = metadata; - this.openHandle = InvokerUtils.bindTo(metadata.getOpenHandle(), endpointInstance); - this.closeHandle = InvokerUtils.bindTo(metadata.getCloseHandle(), endpointInstance); - this.errorHandle = InvokerUtils.bindTo(metadata.getErrorHandle(), endpointInstance); - this.textHandle = InvokerUtils.bindTo(metadata.getTextHandle(), endpointInstance); - this.binaryHandle = InvokerUtils.bindTo(metadata.getBinaryHandle(), endpointInstance); + this.openHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getOpenHandle()), endpointInstance); + this.closeHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getCloseHandle()), endpointInstance); + this.errorHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getErrorHandle()), endpointInstance); + this.textHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getTextHandle()), endpointInstance); + this.binaryHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getBinaryHandle()), endpointInstance); this.textSinkClass = metadata.getTextSink(); this.binarySinkClass = metadata.getBinarySink(); - this.frameHandle = InvokerUtils.bindTo(metadata.getFrameHandle(), endpointInstance); - this.pingHandle = InvokerUtils.bindTo(metadata.getPingHandle(), endpointInstance); - this.pongHandle = InvokerUtils.bindTo(metadata.getPongHandle(), endpointInstance); + this.frameHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getFrameHandle()), endpointInstance); + this.pingHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getPingHandle()), endpointInstance); + this.pongHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getPongHandle()), endpointInstance); } public void setUpgradeRequest(UpgradeRequest upgradeRequest) @@ -156,7 +157,7 @@ public void onOpen(CoreSession coreSession, Callback callback) } } - private static MessageSink createMessageSink(Class sinkClass, WebSocketSession session, MethodHandle msgHandle, boolean autoDemanding) + private static MessageSink createMessageSink(Class sinkClass, WebSocketSession session, MethodHolder msgHandle, boolean autoDemanding) { if (msgHandle == null) return null; @@ -167,7 +168,7 @@ private static MessageSink createMessageSink(Class sinkCl { MethodHandles.Lookup lookup = JettyWebSocketFrameHandlerFactory.getServerMethodHandleLookup(); MethodHandle ctorHandle = lookup.findConstructor(sinkClass, - MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, boolean.class)); + MethodType.methodType(void.class, CoreSession.class, MethodHolder.class, boolean.class)); return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgHandle, autoDemanding); } catch (NoSuchMethodException e) diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java index aa010794a536..6203bfb1a983 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java @@ -46,6 +46,7 @@ import org.eclipse.jetty.websocket.core.messages.ReaderMessageSink; import org.eclipse.jetty.websocket.core.messages.StringMessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.eclipse.jetty.websocket.core.util.ReflectUtils; /** diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java index 4d9b6b3ef267..8ea5061b5d5e 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java @@ -13,28 +13,28 @@ package org.eclipse.jetty.websocket.common.internal; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.nio.ByteBuffer; import org.eclipse.jetty.websocket.api.Callback; import org.eclipse.jetty.websocket.core.CoreSession; -import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class ByteBufferMessageSink extends org.eclipse.jetty.websocket.core.messages.ByteBufferMessageSink { - public ByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public ByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand, false); + super(session, methodHolder, autoDemand, false); - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, Callback.class); - if (methodHandle.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); + /* TODO + MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, Callback.class); + if (methodHolder.type() != onMessageType) + throw InvalidSignatureException.build(onMessageType, methodHolder.type()); + */ } @Override - protected void invoke(MethodHandle methodHandle, ByteBuffer byteBuffer, org.eclipse.jetty.util.Callback callback) throws Throwable + protected void invoke(MethodHolder methodHolder, ByteBuffer byteBuffer, org.eclipse.jetty.util.Callback callback) throws Throwable { - methodHandle.invoke(byteBuffer, Callback.from(callback::succeeded, callback::failed)); + methodHolder.invoke(byteBuffer, Callback.from(callback::succeeded, callback::failed)); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java index 479e90cb7797..45a816946596 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java @@ -20,21 +20,25 @@ import org.eclipse.jetty.websocket.api.Callback; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class PartialByteBufferMessageSink extends org.eclipse.jetty.websocket.core.messages.PartialByteBufferMessageSink { - public PartialByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public PartialByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); + super(session, methodHolder, autoDemand); - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, boolean.class, Callback.class); - if (methodHandle.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); + /* + TODO: + MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, boolean.class, Callback.class); + if (methodHolder.type() != onMessageType) + throw InvalidSignatureException.build(onMessageType, methodHolder.type()); + */ } @Override - protected void invoke(MethodHandle methodHandle, ByteBuffer byteBuffer, boolean fin, org.eclipse.jetty.util.Callback callback) throws Throwable + protected void invoke(MethodHolder methodHolder, ByteBuffer byteBuffer, boolean fin, org.eclipse.jetty.util.Callback callback) throws Throwable { - methodHandle.invoke(byteBuffer, fin, Callback.from(callback::succeeded, callback::failed)); + methodHolder.invoke(byteBuffer, fin, Callback.from(callback::succeeded, callback::failed)); } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java index c8970e284a1c..14a707f6b470 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.messages.StringMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,7 +97,7 @@ public void sendFrame(Frame frame, org.eclipse.jetty.util.Callback callback, boo String event = String.format("TEXT:fin=%b:len=%d", frame.isFin(), frame.getPayloadLength()); LOG.debug(event); events.offer(event); - messageSink = new StringMessageSink(this, wholeTextHandle, true); + messageSink = new StringMessageSink(this, MethodHolder.from(wholeTextHandle), true); break; } case OpCode.BINARY: @@ -104,7 +105,7 @@ public void sendFrame(Frame frame, org.eclipse.jetty.util.Callback callback, boo String event = String.format("BINARY:fin=%b:len=%d", frame.isFin(), frame.getPayloadLength()); LOG.debug(event); events.offer(event); - messageSink = new ByteBufferMessageSink(this, wholeBinaryHandle, true); + messageSink = new ByteBufferMessageSink(this, MethodHolder.from(wholeBinaryHandle), true); break; } case OpCode.CONTINUATION: diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java new file mode 100644 index 000000000000..7b3e66f1849e --- /dev/null +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee10.websocket.jakarta.common; + +import java.lang.invoke.WrongMethodTypeException; + +import jakarta.websocket.MessageHandler; +import org.eclipse.jetty.websocket.core.util.MethodHolder; + +class JakartaMessagePartialMethodHolder implements MethodHolder +{ + private final MessageHandler.Partial _messageHandler; + + public JakartaMessagePartialMethodHolder(MessageHandler.Partial messageHandler) + { + _messageHandler = messageHandler; + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(Object... args) throws Throwable + { + if (args.length != 2) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 2, args.length)); + _messageHandler.onMessage((T)args[0], (boolean)args[1]); + return null; + } + + @Override + public Class parameterType(int idx) + { + switch (idx) + { + case 0: + return Object.class; + case 1: + return boolean.class; + default: + throw new IndexOutOfBoundsException(idx); + } + } + + @Override + public Class returnType() + { + return void.class; + } +} diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java new file mode 100644 index 000000000000..efe8bab30a87 --- /dev/null +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee10.websocket.jakarta.common; + +import java.lang.invoke.WrongMethodTypeException; + +import jakarta.websocket.MessageHandler; +import org.eclipse.jetty.websocket.core.util.MethodHolder; + +class JakartaMessageWholeMethodHolder implements MethodHolder +{ + private final MessageHandler.Whole _messageHandler; + + public JakartaMessageWholeMethodHolder(MessageHandler.Whole messageHandler) + { + _messageHandler = messageHandler; + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(Object... args) throws Throwable + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + _messageHandler.onMessage((T)args[0]); + return null; + } + + @Override + public Class parameterType(int idx) + { + switch (idx) + { + case 0: + return Object.class; + default: + throw new IndexOutOfBoundsException(idx); + } + } + + @Override + public Class returnType() + { + return void.class; + } +} diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandler.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandler.java index 30681596212d..460b6c13d9bd 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandler.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandler.java @@ -51,6 +51,7 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteBufferMessageSink; import org.eclipse.jetty.websocket.core.messages.PartialStringMessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,10 +63,10 @@ public class JakartaWebSocketFrameHandler implements FrameHandler private final Object endpointInstance; private final AtomicBoolean closeNotified = new AtomicBoolean(); - private MethodHandle openHandle; - private MethodHandle closeHandle; - private MethodHandle errorHandle; - private MethodHandle pongHandle; + private MethodHolder openHandle; + private MethodHolder closeHandle; + private MethodHolder errorHandle; + private MethodHolder pongHandle; private JakartaWebSocketMessageMetadata textMetadata; private JakartaWebSocketMessageMetadata binaryMetadata; private final UpgradeRequest upgradeRequest; @@ -79,12 +80,12 @@ public class JakartaWebSocketFrameHandler implements FrameHandler protected byte dataType = OpCode.UNDEFINED; public JakartaWebSocketFrameHandler(JakartaWebSocketContainer container, - UpgradeRequest upgradeRequest, + UpgradeRequest upgradeRequest, Object endpointInstance, - MethodHandle openHandle, MethodHandle closeHandle, MethodHandle errorHandle, - JakartaWebSocketMessageMetadata textMetadata, - JakartaWebSocketMessageMetadata binaryMetadata, - MethodHandle pongHandle, + MethodHolder openHandle, MethodHolder closeHandle, MethodHolder errorHandle, + JakartaWebSocketMessageMetadata textMetadata, + JakartaWebSocketMessageMetadata binaryMetadata, + MethodHolder pongHandle, EndpointConfig endpointConfig) { this.logger = LoggerFactory.getLogger(endpointInstance.getClass()); @@ -147,10 +148,10 @@ public void onOpen(CoreSession coreSession, Callback callback) if (actualTextMetadata.isMaxMessageSizeSet()) session.setMaxTextMessageBufferSize(actualTextMetadata.getMaxMessageSize()); - MethodHandle methodHandle = actualTextMetadata.getMethodHandle(); + MethodHolder methodHandle = actualTextMetadata.getMethodHolder(); methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); methodHandle = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); - actualTextMetadata.setMethodHandle(methodHandle); + actualTextMetadata.setMethodHolder(methodHandle); textSink = JakartaWebSocketFrameHandlerFactory.createMessageSink(session, actualTextMetadata); textMetadata = actualTextMetadata; @@ -162,10 +163,10 @@ public void onOpen(CoreSession coreSession, Callback callback) if (actualBinaryMetadata.isMaxMessageSizeSet()) session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.getMaxMessageSize()); - MethodHandle methodHandle = actualBinaryMetadata.getMethodHandle(); - methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); - methodHandle = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); - actualBinaryMetadata.setMethodHandle(methodHandle); + MethodHolder methodHolder = actualBinaryMetadata.getMethodHolder(); + methodHolder = InvokerUtils.bindTo(methodHolder, endpointInstance, endpointConfig, session); + methodHolder = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHolder, session); + actualBinaryMetadata.setMethodHolder(methodHolder); binarySink = JakartaWebSocketFrameHandlerFactory.createMessageSink(session, actualBinaryMetadata); binaryMetadata = actualBinaryMetadata; @@ -346,116 +347,88 @@ public JakartaWebSocketMessageMetadata getTextMetadata() public void addMessageHandler(Class clazz, MessageHandler.Partial handler) { - try + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(new JakartaMessagePartialMethodHolder<>(handler)); + byte basicType; + // MessageHandler.Partial has no decoder support! + if (byte[].class.isAssignableFrom(clazz)) + { + basicType = OpCode.BINARY; + metadata.setSinkClass(PartialByteArrayMessageSink.class); + } + else if (ByteBuffer.class.isAssignableFrom(clazz)) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Partial.class, "onMessage", MethodType.methodType(void.class, Object.class, boolean.class)) - .bindTo(handler); - - JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); - metadata.setMethodHandle(methodHandle); - byte basicType; - // MessageHandler.Partial has no decoder support! - if (byte[].class.isAssignableFrom(clazz)) - { - basicType = OpCode.BINARY; - metadata.setSinkClass(PartialByteArrayMessageSink.class); - } - else if (ByteBuffer.class.isAssignableFrom(clazz)) - { - basicType = OpCode.BINARY; - metadata.setSinkClass(PartialByteBufferMessageSink.class); - } - else if (String.class.isAssignableFrom(clazz)) - { - basicType = OpCode.TEXT; - metadata.setSinkClass(PartialStringMessageSink.class); - } - else - { - throw new RuntimeException( - "Unable to add " + handler.getClass().getName() + " with type " + clazz + ": only supported types byte[], " + ByteBuffer.class.getName() + - ", " + String.class.getName()); - } - - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + basicType = OpCode.BINARY; + metadata.setSinkClass(PartialByteBufferMessageSink.class); } - catch (NoSuchMethodException e) + else if (String.class.isAssignableFrom(clazz)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.TEXT; + metadata.setSinkClass(PartialStringMessageSink.class); } - catch (IllegalAccessException e) + else { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + throw new RuntimeException( + "Unable to add " + handler.getClass().getName() + " with type " + clazz + ": only supported types byte[], " + ByteBuffer.class.getName() + + ", " + String.class.getName()); } + + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } public void addMessageHandler(Class clazz, MessageHandler.Whole handler) { - try - { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class)) - .bindTo(handler); - - if (PongMessage.class.isAssignableFrom(clazz)) - { - assertBasicTypeNotRegistered(OpCode.PONG, handler); - this.pongHandle = methodHandle; - registerMessageHandler(OpCode.PONG, clazz, handler, null); - return; - } + MethodHolder methodHolder = new JakartaMessageWholeMethodHolder<>(handler); - AvailableDecoders availableDecoders = session.getDecoders(); - RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); - if (registeredDecoder == null) - throw new IllegalStateException("Unable to find Decoder for type: " + clazz); + if (PongMessage.class.isAssignableFrom(clazz)) + { + assertBasicTypeNotRegistered(OpCode.PONG, handler); + this.pongHandle = methodHolder; + registerMessageHandler(OpCode.PONG, clazz, handler, null); + return; + } - // Create the message metadata specific to the MessageHandler type. - JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); - metadata.setMethodHandle(methodHandle); - byte basicType; - if (registeredDecoder.implementsInterface(Decoder.Binary.class)) - { - basicType = OpCode.BINARY; - metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); - metadata.setSinkClass(DecodedBinaryMessageSink.class); - } - else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) - { - basicType = OpCode.BINARY; - metadata.setRegisteredDecoders(availableDecoders.getBinaryStreamDecoders(clazz)); - metadata.setSinkClass(DecodedBinaryStreamMessageSink.class); - } - else if (registeredDecoder.implementsInterface(Decoder.Text.class)) - { - basicType = OpCode.TEXT; - metadata.setRegisteredDecoders(availableDecoders.getTextDecoders(clazz)); - metadata.setSinkClass(DecodedTextMessageSink.class); - } - else if (registeredDecoder.implementsInterface(Decoder.TextStream.class)) - { - basicType = OpCode.TEXT; - metadata.setRegisteredDecoders(availableDecoders.getTextStreamDecoders(clazz)); - metadata.setSinkClass(DecodedTextStreamMessageSink.class); - } - else - { - throw new RuntimeException("Unable to add " + handler.getClass().getName() + ": type " + clazz + " is unrecognized by declared decoders"); - } + AvailableDecoders availableDecoders = session.getDecoders(); + RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); + if (registeredDecoder == null) + throw new IllegalStateException("Unable to find Decoder for type: " + clazz); - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + // Create the message metadata specific to the MessageHandler type. + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(methodHolder); + byte basicType; + if (registeredDecoder.implementsInterface(Decoder.Binary.class)) + { + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryMessageSink.class); + } + else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) + { + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryStreamDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryStreamMessageSink.class); } - catch (NoSuchMethodException e) + else if (registeredDecoder.implementsInterface(Decoder.Text.class)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.TEXT; + metadata.setRegisteredDecoders(availableDecoders.getTextDecoders(clazz)); + metadata.setSinkClass(DecodedTextMessageSink.class); } - catch (IllegalAccessException e) + else if (registeredDecoder.implementsInterface(Decoder.TextStream.class)) { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + basicType = OpCode.TEXT; + metadata.setRegisteredDecoders(availableDecoders.getTextStreamDecoders(clazz)); + metadata.setSinkClass(DecodedTextStreamMessageSink.class); } + else + { + throw new RuntimeException("Unable to add " + handler.getClass().getName() + ": type " + clazz + " is unrecognized by declared decoders"); + } + + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } private void assertBasicTypeNotRegistered(byte basicWebSocketType, MessageHandler replacement) diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java index dd90ab38a138..e45a3a830dd7 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteBufferMessageSink; import org.eclipse.jetty.websocket.core.messages.PartialStringMessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.eclipse.jetty.websocket.core.util.ReflectUtils; public abstract class JakartaWebSocketFrameHandlerFactory @@ -135,10 +136,10 @@ public JakartaWebSocketFrameHandler newJakartaWebSocketFrameHandler(Object endpo if (metadata == null) return null; - MethodHandle openHandle = metadata.getOpenHandle(); - MethodHandle closeHandle = metadata.getCloseHandle(); - MethodHandle errorHandle = metadata.getErrorHandle(); - MethodHandle pongHandle = metadata.getPongHandle(); + MethodHolder openHandle = MethodHolder.from(metadata.getOpenHandle()); + MethodHolder closeHandle = MethodHolder.from(metadata.getCloseHandle()); + MethodHolder errorHandle = MethodHolder.from(metadata.getErrorHandle()); + MethodHolder pongHandle = MethodHolder.from(metadata.getPongHandle()); JakartaWebSocketMessageMetadata textMetadata = JakartaWebSocketMessageMetadata.copyOf(metadata.getTextMetadata()); JakartaWebSocketMessageMetadata binaryMetadata = JakartaWebSocketMessageMetadata.copyOf(metadata.getBinaryMetadata()); @@ -156,9 +157,9 @@ public JakartaWebSocketFrameHandler newJakartaWebSocketFrameHandler(Object endpo pongHandle = bindTemplateVariables(pongHandle, namedVariables, pathParams); if (textMetadata != null) - textMetadata.setMethodHandle(bindTemplateVariables(textMetadata.getMethodHandle(), namedVariables, pathParams)); + textMetadata.setMethodHolder(bindTemplateVariables(textMetadata.getMethodHolder(), namedVariables, pathParams)); if (binaryMetadata != null) - binaryMetadata.setMethodHandle(bindTemplateVariables(binaryMetadata.getMethodHandle(), namedVariables, pathParams)); + binaryMetadata.setMethodHolder(bindTemplateVariables(binaryMetadata.getMethodHolder(), namedVariables, pathParams)); } openHandle = InvokerUtils.bindTo(openHandle, endpoint); @@ -192,13 +193,13 @@ public static MessageSink createMessageSink(JakartaWebSocketSession session, Jak MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, List.class)); List registeredDecoders = msgMetadata.getRegisteredDecoders(); - return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle(), registeredDecoders); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), registeredDecoders); } else { MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, boolean.class)); - return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle(), true); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } catch (NoSuchMethodException e) @@ -219,23 +220,19 @@ public static MessageSink createMessageSink(JakartaWebSocketSession session, Jak } } - public static MethodHandle wrapNonVoidReturnType(MethodHandle handle, JakartaWebSocketSession session) + public static MethodHolder wrapNonVoidReturnType(MethodHolder handle, JakartaWebSocketSession session) { if (handle == null) return null; - if (handle.type().returnType() == Void.TYPE) + if (handle.returnType() == Void.TYPE) return handle; - // Technique from https://stackoverflow.com/questions/48505787/methodhandle-with-general-non-void-return-filter - - // Change the return type of the to be Object so it will match exact with JakartaWebSocketSession.filterReturnType(Object) - handle = handle.asType(handle.type().changeReturnType(Object.class)); - - // Filter the method return type to a call to JakartaWebSocketSession.filterReturnType() bound to this session - handle = MethodHandles.filterReturnValue(handle, FILTER_RETURN_TYPE_METHOD.bindTo(session)); - - return handle; + return args -> + { + session.filterReturnType(handle.invoke(args)); + return null; + }; } private MethodHandle toMethodHandle(MethodHandles.Lookup lookup, Method method) @@ -360,7 +357,7 @@ private boolean matchOnMessage(Method onMsg, JakartaWebSocketFrameHandlerMetadat if (methodHandle != null) { msgMetadata.setSinkClass(PartialStringMessageSink.class); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); metadata.setTextMetadata(msgMetadata, onMsg); return true; } @@ -370,7 +367,7 @@ private boolean matchOnMessage(Method onMsg, JakartaWebSocketFrameHandlerMetadat if (methodHandle != null) { msgMetadata.setSinkClass(PartialByteBufferMessageSink.class); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } @@ -380,7 +377,7 @@ private boolean matchOnMessage(Method onMsg, JakartaWebSocketFrameHandlerMetadat if (methodHandle != null) { msgMetadata.setSinkClass(PartialByteArrayMessageSink.class); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } @@ -423,7 +420,7 @@ private boolean matchDecoders(Method onMsg, JakartaWebSocketFrameHandlerMetadata objectType = decoder.objectType; } MethodHandle methodHandle = getMethodHandle.apply(getArgsFor(objectType)); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); // Set the sinkClass and then set the MessageMetadata on the FrameHandlerMetadata if (interfaceType.equals(Decoder.Text.class)) @@ -508,7 +505,7 @@ private void assertSignatureValid(Class endpointClass, Method method, Class templateValues) + public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map templateValues) { if (target == null) { @@ -517,7 +514,7 @@ public static MethodHandle bindTemplateVariables(MethodHandle target, String[] n final int IDX = 1; - MethodHandle retHandle = target; + MethodHolder retHandle = target; if ((templateValues == null) || (templateValues.isEmpty())) { @@ -527,54 +524,54 @@ public static MethodHandle bindTemplateVariables(MethodHandle target, String[] n for (String variableName : namedVariables) { String strValue = templateValues.get(variableName); - Class type = retHandle.type().parameterType(IDX); + Class type = retHandle.parameterType(IDX); try { if (String.class.isAssignableFrom(type)) { - retHandle = MethodHandles.insertArguments(retHandle, IDX, strValue); + retHandle = retHandle.bindTo(strValue, IDX); } else if (Integer.class.isAssignableFrom(type) || Integer.TYPE.isAssignableFrom(type)) { Integer intValue = Integer.parseInt(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, intValue); + retHandle = retHandle.bindTo(intValue, IDX); } else if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) { Long longValue = Long.parseLong(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, longValue); + retHandle = retHandle.bindTo(longValue, IDX); } else if (Short.class.isAssignableFrom(type) || Short.TYPE.isAssignableFrom(type)) { Short shortValue = Short.parseShort(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, shortValue); + retHandle = retHandle.bindTo(shortValue, IDX); } else if (Float.class.isAssignableFrom(type) || Float.TYPE.isAssignableFrom(type)) { Float floatValue = Float.parseFloat(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, floatValue); + retHandle = retHandle.bindTo(floatValue, IDX); } else if (Double.class.isAssignableFrom(type) || Double.TYPE.isAssignableFrom(type)) { Double doubleValue = Double.parseDouble(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, doubleValue); + retHandle = retHandle.bindTo(doubleValue, IDX); } else if (Boolean.class.isAssignableFrom(type) || Boolean.TYPE.isAssignableFrom(type)) { Boolean boolValue = Boolean.parseBoolean(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, boolValue); + retHandle = retHandle.bindTo(boolValue, IDX); } else if (Character.class.isAssignableFrom(type) || Character.TYPE.isAssignableFrom(type)) { if (strValue.length() != 1) throw new IllegalArgumentException("Invalid Size"); Character charValue = strValue.charAt(0); - retHandle = MethodHandles.insertArguments(retHandle, IDX, charValue); + retHandle = retHandle.bindTo(charValue, IDX); } else if (Byte.class.isAssignableFrom(type) || Byte.TYPE.isAssignableFrom(type)) { Byte b = Byte.parseByte(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, b); + retHandle = retHandle.bindTo(b, IDX); } else { diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java index a5587ae30ae4..7511f372e8cb 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java @@ -13,15 +13,15 @@ package org.eclipse.jetty.ee10.websocket.jakarta.common; -import java.lang.invoke.MethodHandle; import java.util.List; import org.eclipse.jetty.ee10.websocket.jakarta.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class JakartaWebSocketMessageMetadata { - private MethodHandle methodHandle; + private MethodHolder methodHolder; private Class sinkClass; private List registeredDecoders; @@ -34,7 +34,7 @@ public static JakartaWebSocketMessageMetadata copyOf(JakartaWebSocketMessageMeta return null; JakartaWebSocketMessageMetadata copy = new JakartaWebSocketMessageMetadata(); - copy.methodHandle = metadata.methodHandle; + copy.methodHolder = metadata.methodHolder; copy.sinkClass = metadata.sinkClass; copy.registeredDecoders = metadata.registeredDecoders; copy.maxMessageSize = metadata.maxMessageSize; @@ -58,14 +58,14 @@ public void setMaxMessageSize(int maxMessageSize) this.maxMessageSizeSet = true; } - public MethodHandle getMethodHandle() + public MethodHolder getMethodHolder() { - return methodHandle; + return methodHolder; } - public void setMethodHandle(MethodHandle methodHandle) + public void setMethodHolder(MethodHolder methodHandle) { - this.methodHandle = methodHandle; + this.methodHolder = methodHandle; } public Class getSinkClass() diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java index a62d243fb891..b952888ac8fa 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,12 +33,12 @@ public abstract class AbstractDecodedMessageSink implements MessageSink { private static final Logger LOG = LoggerFactory.getLogger(AbstractDecodedMessageSink.class); - private final MethodHandle _methodHandle; + private final MethodHolder _methodHolder; private final MessageSink _messageSink; - public AbstractDecodedMessageSink(CoreSession coreSession, MethodHandle methodHandle) + public AbstractDecodedMessageSink(CoreSession coreSession, MethodHolder methodHolder) { - _methodHandle = methodHandle; + _methodHolder = methodHolder; try { @@ -58,7 +59,7 @@ void invoke(Object message) { try { - _methodHandle.invoke(message); + _methodHolder.invoke(message); } catch (Throwable t) { @@ -67,7 +68,7 @@ void invoke(Object message) } /** - * @return a message sink which will first decode the message then pass it to {@link #_methodHandle}. + * @return a message sink which will first decode the message then pass it to {@link #_methodHolder}. * @throws Exception for any error in creating the message sink. */ abstract MessageSink newMessageSink(CoreSession coreSession) throws Exception; @@ -90,9 +91,9 @@ public abstract static class Basic extends AbstractDecodedMes { protected final List _decoders; - public Basic(CoreSession coreSession, MethodHandle methodHandle, List decoders) + public Basic(CoreSession coreSession, MethodHolder methodHolder, List decoders) { - super(coreSession, methodHandle); + super(coreSession, methodHolder); if (decoders.isEmpty()) throw new IllegalArgumentException("Require at least one decoder for " + this.getClass()); _decoders = decoders.stream() @@ -105,9 +106,9 @@ public abstract static class Stream extends AbstractDecodedMe { protected final T _decoder; - public Stream(CoreSession coreSession, MethodHandle methodHandle, List decoders) + public Stream(CoreSession coreSession, MethodHolder methodHolder, List decoders) { - super(coreSession, methodHandle); + super(coreSession, methodHolder); if (decoders.size() != 1) throw new IllegalArgumentException("Require exactly one decoder for " + this.getClass()); _decoder = decoders.get(0).getInstance(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java index d3715baf8940..28b7b8c1924f 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java @@ -13,20 +13,19 @@ package org.eclipse.jetty.ee10.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.nio.ByteBuffer; import java.util.List; import jakarta.websocket.CloseReason; import jakarta.websocket.DecodeException; import jakarta.websocket.Decoder; -import org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee10.websocket.jakarta.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.ByteBufferMessageSink; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,18 +33,23 @@ public class DecodedBinaryMessageSink extends AbstractDecodedMessageSink.Basi { private static final Logger LOG = LoggerFactory.getLogger(DecodedBinaryMessageSink.class); - public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedBinaryMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws Exception + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(DecodedBinaryMessageSink.class, "onWholeMessage", MethodType.methodType(void.class, ByteBuffer.class)) - .bindTo(this); - return new ByteBufferMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onWholeMessage((ByteBuffer)args[0]); + return null; + }; + + return new ByteBufferMessageSink(coreSession, methodHolder, true); } public void onWholeMessage(ByteBuffer wholeMessage) diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java index 6e7b34c3ce0a..f58d2cfc5f01 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.util.List; import jakarta.websocket.CloseReason; @@ -28,21 +29,27 @@ import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.InputStreamMessageSink; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class DecodedBinaryStreamMessageSink extends AbstractDecodedMessageSink.Stream> { - public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedBinaryStreamMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws Exception + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(DecodedBinaryStreamMessageSink.class, "onStreamStart", MethodType.methodType(void.class, InputStream.class)) - .bindTo(this); - return new InputStreamMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onStreamStart((InputStream)args[0]); + return null; + }; + + return new InputStreamMessageSink(coreSession, methodHolder, true); } public void onStreamStart(InputStream stream) diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSink.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSink.java index 6b9f98b6b134..a6db6771b77b 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSink.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSink.java @@ -13,19 +13,18 @@ package org.eclipse.jetty.ee10.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.util.List; import jakarta.websocket.CloseReason; import jakarta.websocket.DecodeException; import jakarta.websocket.Decoder; -import org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee10.websocket.jakarta.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.messages.StringMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,18 +32,23 @@ public class DecodedTextMessageSink extends AbstractDecodedMessageSink.Basic< { private static final Logger LOG = LoggerFactory.getLogger(DecodedTextMessageSink.class); - public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedTextMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws NoSuchMethodException, IllegalAccessException + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(getClass(), "onMessage", MethodType.methodType(void.class, String.class)) - .bindTo(this); - return new StringMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onMessage((String)args[0]); + return null; + }; + + return new StringMessageSink(coreSession, methodHolder, true); } public void onMessage(String wholeMessage) diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java index 59bed6e8b854..5fbb7252b807 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java @@ -17,6 +17,7 @@ import java.io.Reader; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.util.List; import jakarta.websocket.CloseReason; @@ -28,21 +29,27 @@ import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.messages.ReaderMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class DecodedTextStreamMessageSink extends AbstractDecodedMessageSink.Stream> { - public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedTextStreamMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws Exception + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(DecodedTextStreamMessageSink.class, "onStreamStart", MethodType.methodType(void.class, Reader.class)) - .bindTo(this); - return new ReaderMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onStreamStart((Reader)args[0]); + return null; + }; + + return new ReaderMessageSink(coreSession, methodHolder, true); } public void onStreamStart(Reader reader) diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java index 4e21e76d317d..a927fd560f23 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -46,7 +47,7 @@ public void testCalendar1Frame() throws Exception DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback finCallback = new FutureCallback(); ByteBuffer data = ByteBuffer.allocate(16); @@ -69,7 +70,7 @@ public void testCalendar3Frames() throws Exception DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java index 19f2d82c00c3..db830c3d2a19 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -49,7 +50,7 @@ public void testCalendar1Frame() throws Exception DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback finCallback = new FutureCallback(); ByteBuffer data = ByteBuffer.allocate(16); @@ -72,7 +73,7 @@ public void testCalendar3Frames() throws Exception DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java index e3fccf35041a..eac4ee36f05d 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -47,7 +48,7 @@ public void testDate1Frame() throws Exception DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback finCallback = new FutureCallback(); sink.accept(new Frame(OpCode.TEXT).setPayload("2018.02.13").setFin(true), finCallback); @@ -65,7 +66,7 @@ public void testDate3Frames() throws Exception DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java index fe83857050ca..9647c6068864 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -50,7 +51,7 @@ public void testDate1Frame() throws Exception DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback finCallback = new FutureCallback(); sink.accept(new Frame(OpCode.TEXT).setPayload("2018.02.13").setFin(true), finCallback); @@ -68,7 +69,7 @@ public void testDate3Frames() throws Exception DecodedDateCopy copy = new DecodedDateCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java index b91f0e28702e..3e08f41e03fc 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.messages.InputStreamMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static java.nio.charset.StandardCharsets.UTF_8; @@ -46,7 +47,7 @@ public void testInputStream1Message1Frame() throws InterruptedException, Executi { InputStreamCopy copy = new InputStreamCopy(); MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); - InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), copyHandle, true); + InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), MethodHolder.from(copyHandle), true); FutureCallback finCallback = new FutureCallback(); ByteBuffer data = BufferUtil.toBuffer("Hello World", UTF_8); @@ -64,7 +65,7 @@ public void testInputStream2Messages2Frames() throws InterruptedException, Execu { InputStreamCopy copy = new InputStreamCopy(); MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); - InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), copyHandle, true); + InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), MethodHolder.from(copyHandle), true); FutureCallback fin1Callback = new FutureCallback(); ByteBuffer data1 = BufferUtil.toBuffer("Hello World", UTF_8); @@ -95,7 +96,7 @@ public void testInputStream1Message3Frames() throws InterruptedException, Execut { InputStreamCopy copy = new InputStreamCopy(); MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); - InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), copyHandle, true); + InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), MethodHolder.from(copyHandle), true); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); @@ -120,7 +121,7 @@ public void testInputStream1Message4FramesEmptyFin() throws InterruptedException { InputStreamCopy copy = new InputStreamCopy(); MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); - InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), copyHandle, true); + InputStreamMessageSink sink = new InputStreamMessageSink(session.getCoreSession(), MethodHolder.from(copyHandle), true); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/ReaderMessageSinkTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/ReaderMessageSinkTest.java index af3264e8d91d..5cc83ebc99cf 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/ReaderMessageSinkTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/ReaderMessageSinkTest.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.messages.ReaderMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -41,7 +42,7 @@ public void testReader1Frame() throws InterruptedException, ExecutionException, CompletableFuture copyFuture = new CompletableFuture<>(); ReaderCopy copy = new ReaderCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Reader.class); - ReaderMessageSink sink = new ReaderMessageSink(session.getCoreSession(), copyHandle, true); + ReaderMessageSink sink = new ReaderMessageSink(session.getCoreSession(), MethodHolder.from(copyHandle), true); FutureCallback finCallback = new FutureCallback(); sink.accept(new Frame(OpCode.TEXT).setPayload("Hello World"), finCallback); @@ -58,7 +59,7 @@ public void testReader3Frames() throws InterruptedException, ExecutionException, CompletableFuture copyFuture = new CompletableFuture<>(); ReaderCopy copy = new ReaderCopy(copyFuture); MethodHandle copyHandle = getAcceptHandle(copy, Reader.class); - ReaderMessageSink sink = new ReaderMessageSink(session.getCoreSession(), copyHandle, true); + ReaderMessageSink sink = new ReaderMessageSink(session.getCoreSession(), MethodHolder.from(copyHandle), true); FutureCallback callback1 = new FutureCallback(); FutureCallback callback2 = new FutureCallback(); diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java index 578a5ae83e6b..6fd32c011a72 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.ee10.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.eclipse.jetty.websocket.core.util.ReflectUtils; import org.junit.jupiter.api.Test; @@ -57,6 +58,12 @@ public String onColorMessage(Session session, String message, @Name("color") Str private static MethodHandles.Lookup lookup = MethodHandles.lookup(); + private MethodHolder getMethodHolder(Method method, String[] namedVariables, InvokerUtils.Arg... args) + { + MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables, args); + return MethodHolder.from(methodHandle); + } + @Test public void testOnlyParamString() throws Throwable { @@ -70,21 +77,21 @@ public void testOnlyParamString() throws Throwable // Raw Calling Args - none specified // Get basic method handle (without a instance to call against) - this is what the metadata stores - MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables); + MethodHolder methodHolder = getMethodHolder(method, namedVariables); // Some point later an actual instance is needed, which has static named parameters Map templateValues = new HashMap<>(); templateValues.put("fruit", "pear"); // Bind the static values, in same order as declared - methodHandle = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHandle, namedVariables, templateValues); + methodHolder = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHolder, namedVariables, templateValues); // Assign an instance to call. Foo foo = new Foo(); - methodHandle = methodHandle.bindTo(foo); + methodHolder = methodHolder.bindTo(foo); // Call method against instance - String result = (String)methodHandle.invoke(); + String result = (String)methodHolder.invoke(); assertThat("Result", result, is("onFruit('pear')")); } @@ -99,21 +106,21 @@ public void testOnlyParamInt() throws Throwable }; // Get basic method handle (without a instance to call against) - this is what the metadata stores - MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables); + MethodHolder methodHolder = getMethodHolder(method, namedVariables); // Some point later an actual instance is needed, which has static named parameters Map templateValues = new HashMap<>(); templateValues.put("count", "2222"); // Bind the static values for the variables, in same order as the variables were declared - methodHandle = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHandle, namedVariables, templateValues); + methodHolder = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHolder, namedVariables, templateValues); // Assign an instance to call. Foo foo = new Foo(); - methodHandle = methodHandle.bindTo(foo); + methodHolder = methodHolder.bindTo(foo); // Call method against instance - String result = (String)methodHandle.invoke(); + String result = (String)methodHolder.invoke(); assertThat("Result", result, is("onCount(2222)")); } @@ -130,21 +137,21 @@ public void testLabeledParamStringInt() throws Throwable final InvokerUtils.Arg ARG_LABEL = new InvokerUtils.Arg(String.class).required(); // Get basic method handle (without a instance to call against) - this is what the metadata stores - MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables, ARG_LABEL); + MethodHolder methodHolder = getMethodHolder(method, namedVariables, ARG_LABEL); // Some point later an actual instance is needed, which has static named parameters Map templateValues = new HashMap<>(); templateValues.put("count", "444"); // Bind the static values for the variables, in same order as the variables were declared - methodHandle = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHandle, namedVariables, templateValues); + methodHolder = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHolder, namedVariables, templateValues); // Assign an instance to call. Foo foo = new Foo(); - methodHandle = methodHandle.bindTo(foo); + methodHolder = methodHolder.bindTo(foo); // Call method against instance - String result = (String)methodHandle.invoke("cherry"); + String result = (String)methodHolder.invoke("cherry"); assertThat("Result", result, is("onLabeledCount('cherry', 444)")); } } diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderTextStreamTest.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderTextStreamTest.java index 73b8f3f32446..3e3f83780fbe 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderTextStreamTest.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderTextStreamTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.WebSocketComponents; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -80,7 +81,7 @@ public void testQuotesDecodedReaderMessageSink() throws Exception }); List decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(quoteHandle), decoders); List callbacks = new ArrayList<>(); FutureCallback finCallback = null; diff --git a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/DefaultServletTest.java b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/DefaultServletTest.java index 1dd1f70e9d90..e0040eba2210 100644 --- a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/DefaultServletTest.java +++ b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/DefaultServletTest.java @@ -1562,6 +1562,7 @@ public void testGzip() throws Exception rawResponse = connector.getResponse("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n"); response = HttpTester.parseResponse(rawResponse); + System.err.println(response); assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response, containsHeaderValue(HttpHeader.CONTENT_LENGTH, "12")); assertThat(response, containsHeaderValue(HttpHeader.CONTENT_TYPE, "text/plain")); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java new file mode 100644 index 000000000000..76ecd0effa97 --- /dev/null +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee9.websocket.jakarta.common; + +import java.lang.invoke.WrongMethodTypeException; + +import jakarta.websocket.MessageHandler; +import org.eclipse.jetty.websocket.core.util.MethodHolder; + +class JakartaMessagePartialMethodHolder implements MethodHolder +{ + private final MessageHandler.Partial _messageHandler; + + public JakartaMessagePartialMethodHolder(MessageHandler.Partial messageHandler) + { + _messageHandler = messageHandler; + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(Object... args) throws Throwable + { + if (args.length != 2) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 2, args.length)); + _messageHandler.onMessage((T)args[0], (boolean)args[1]); + return null; + } + + @Override + public Class parameterType(int idx) + { + switch (idx) + { + case 0: + return Object.class; + case 1: + return boolean.class; + default: + throw new IndexOutOfBoundsException(idx); + } + } + + @Override + public Class returnType() + { + return void.class; + } +} diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java new file mode 100644 index 000000000000..73c0ce3ca132 --- /dev/null +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee9.websocket.jakarta.common; + +import java.lang.invoke.WrongMethodTypeException; + +import jakarta.websocket.MessageHandler; +import org.eclipse.jetty.websocket.core.util.MethodHolder; + +class JakartaMessageWholeMethodHolder implements MethodHolder +{ + private final MessageHandler.Whole _messageHandler; + + public JakartaMessageWholeMethodHolder(MessageHandler.Whole messageHandler) + { + _messageHandler = messageHandler; + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(Object... args) throws Throwable + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + _messageHandler.onMessage((T)args[0]); + return null; + } + + @Override + public Class parameterType(int idx) + { + switch (idx) + { + case 0: + return Object.class; + default: + throw new IndexOutOfBoundsException(idx); + } + } + + @Override + public Class returnType() + { + return void.class; + } +} diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandler.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandler.java index a7d36d2412a6..73d65ab7647e 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandler.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandler.java @@ -13,8 +13,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; @@ -52,6 +50,7 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteBufferMessageSink; import org.eclipse.jetty.websocket.core.messages.PartialStringMessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,10 +62,10 @@ public class JakartaWebSocketFrameHandler implements FrameHandler private final Object endpointInstance; private final AtomicBoolean closeNotified = new AtomicBoolean(); - private MethodHandle openHandle; - private MethodHandle closeHandle; - private MethodHandle errorHandle; - private MethodHandle pongHandle; + private MethodHolder openHandle; + private MethodHolder closeHandle; + private MethodHolder errorHandle; + private MethodHolder pongHandle; private JakartaWebSocketMessageMetadata textMetadata; private JakartaWebSocketMessageMetadata binaryMetadata; private final UpgradeRequest upgradeRequest; @@ -81,12 +80,12 @@ public class JakartaWebSocketFrameHandler implements FrameHandler public JakartaWebSocketFrameHandler(JakartaWebSocketContainer container, UpgradeRequest upgradeRequest, - Object endpointInstance, - MethodHandle openHandle, MethodHandle closeHandle, MethodHandle errorHandle, + Object endpointInstance, + MethodHolder openHandle, MethodHolder closeHandle, MethodHolder errorHandle, JakartaWebSocketMessageMetadata textMetadata, JakartaWebSocketMessageMetadata binaryMetadata, - MethodHandle pongHandle, - EndpointConfig endpointConfig) + MethodHolder pongHandle, + EndpointConfig endpointConfig) { this.logger = LoggerFactory.getLogger(endpointInstance.getClass()); @@ -148,10 +147,10 @@ public void onOpen(CoreSession coreSession, Callback callback) if (actualTextMetadata.isMaxMessageSizeSet()) session.setMaxTextMessageBufferSize(actualTextMetadata.getMaxMessageSize()); - MethodHandle methodHandle = actualTextMetadata.getMethodHandle(); - methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); - methodHandle = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); - actualTextMetadata.setMethodHandle(methodHandle); + MethodHolder methodHolder = actualTextMetadata.getMethodHolder(); + methodHolder = InvokerUtils.bindTo(methodHolder, endpointInstance, endpointConfig, session); + methodHolder = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHolder, session); + actualTextMetadata.setMethodHolder(methodHolder); textSink = JakartaWebSocketFrameHandlerFactory.createMessageSink(session, actualTextMetadata); textMetadata = actualTextMetadata; @@ -163,10 +162,10 @@ public void onOpen(CoreSession coreSession, Callback callback) if (actualBinaryMetadata.isMaxMessageSizeSet()) session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.getMaxMessageSize()); - MethodHandle methodHandle = actualBinaryMetadata.getMethodHandle(); - methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); - methodHandle = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); - actualBinaryMetadata.setMethodHandle(methodHandle); + MethodHolder methodHolder = actualBinaryMetadata.getMethodHolder(); + methodHolder = InvokerUtils.bindTo(methodHolder, endpointInstance, endpointConfig, session); + methodHolder = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHolder, session); + actualBinaryMetadata.setMethodHolder(methodHolder); binarySink = JakartaWebSocketFrameHandlerFactory.createMessageSink(session, actualBinaryMetadata); binaryMetadata = actualBinaryMetadata; @@ -353,116 +352,88 @@ public JakartaWebSocketMessageMetadata getTextMetadata() public void addMessageHandler(Class clazz, MessageHandler.Partial handler) { - try + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(new JakartaMessagePartialMethodHolder<>(handler)); + byte basicType; + // MessageHandler.Partial has no decoder support! + if (byte[].class.isAssignableFrom(clazz)) + { + basicType = OpCode.BINARY; + metadata.setSinkClass(PartialByteArrayMessageSink.class); + } + else if (ByteBuffer.class.isAssignableFrom(clazz)) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Partial.class, "onMessage", MethodType.methodType(void.class, Object.class, boolean.class)) - .bindTo(handler); - - JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); - metadata.setMethodHandle(methodHandle); - byte basicType; - // MessageHandler.Partial has no decoder support! - if (byte[].class.isAssignableFrom(clazz)) - { - basicType = OpCode.BINARY; - metadata.setSinkClass(PartialByteArrayMessageSink.class); - } - else if (ByteBuffer.class.isAssignableFrom(clazz)) - { - basicType = OpCode.BINARY; - metadata.setSinkClass(PartialByteBufferMessageSink.class); - } - else if (String.class.isAssignableFrom(clazz)) - { - basicType = OpCode.TEXT; - metadata.setSinkClass(PartialStringMessageSink.class); - } - else - { - throw new RuntimeException( - "Unable to add " + handler.getClass().getName() + " with type " + clazz + ": only supported types byte[], " + ByteBuffer.class.getName() + - ", " + String.class.getName()); - } - - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + basicType = OpCode.BINARY; + metadata.setSinkClass(PartialByteBufferMessageSink.class); } - catch (NoSuchMethodException e) + else if (String.class.isAssignableFrom(clazz)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.TEXT; + metadata.setSinkClass(PartialStringMessageSink.class); } - catch (IllegalAccessException e) + else { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + throw new RuntimeException( + "Unable to add " + handler.getClass().getName() + " with type " + clazz + ": only supported types byte[], " + ByteBuffer.class.getName() + + ", " + String.class.getName()); } + + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } public void addMessageHandler(Class clazz, MessageHandler.Whole handler) { - try - { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class)) - .bindTo(handler); + MethodHolder methodHolder = new JakartaMessageWholeMethodHolder<>(handler); - if (PongMessage.class.isAssignableFrom(clazz)) - { - assertBasicTypeNotRegistered(OpCode.PONG, handler); - this.pongHandle = methodHandle; - registerMessageHandler(OpCode.PONG, clazz, handler, null); - return; - } - - AvailableDecoders availableDecoders = session.getDecoders(); - RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); - if (registeredDecoder == null) - throw new IllegalStateException("Unable to find Decoder for type: " + clazz); + if (PongMessage.class.isAssignableFrom(clazz)) + { + assertBasicTypeNotRegistered(OpCode.PONG, handler); + this.pongHandle = methodHolder; + registerMessageHandler(OpCode.PONG, clazz, handler, null); + return; + } - // Create the message metadata specific to the MessageHandler type. - JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); - metadata.setMethodHandle(methodHandle); - byte basicType; - if (registeredDecoder.implementsInterface(Decoder.Binary.class)) - { - basicType = OpCode.BINARY; - metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); - metadata.setSinkClass(DecodedBinaryMessageSink.class); - } - else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) - { - basicType = OpCode.BINARY; - metadata.setRegisteredDecoders(availableDecoders.getBinaryStreamDecoders(clazz)); - metadata.setSinkClass(DecodedBinaryStreamMessageSink.class); - } - else if (registeredDecoder.implementsInterface(Decoder.Text.class)) - { - basicType = OpCode.TEXT; - metadata.setRegisteredDecoders(availableDecoders.getTextDecoders(clazz)); - metadata.setSinkClass(DecodedTextMessageSink.class); - } - else if (registeredDecoder.implementsInterface(Decoder.TextStream.class)) - { - basicType = OpCode.TEXT; - metadata.setRegisteredDecoders(availableDecoders.getTextStreamDecoders(clazz)); - metadata.setSinkClass(DecodedTextStreamMessageSink.class); - } - else - { - throw new RuntimeException("Unable to add " + handler.getClass().getName() + ": type " + clazz + " is unrecognized by declared decoders"); - } + AvailableDecoders availableDecoders = session.getDecoders(); + RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); + if (registeredDecoder == null) + throw new IllegalStateException("Unable to find Decoder for type: " + clazz); - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + // Create the message metadata specific to the MessageHandler type. + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(methodHolder); + byte basicType; + if (registeredDecoder.implementsInterface(Decoder.Binary.class)) + { + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryMessageSink.class); + } + else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) + { + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryStreamDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryStreamMessageSink.class); } - catch (NoSuchMethodException e) + else if (registeredDecoder.implementsInterface(Decoder.Text.class)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.TEXT; + metadata.setRegisteredDecoders(availableDecoders.getTextDecoders(clazz)); + metadata.setSinkClass(DecodedTextMessageSink.class); } - catch (IllegalAccessException e) + else if (registeredDecoder.implementsInterface(Decoder.TextStream.class)) { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + basicType = OpCode.TEXT; + metadata.setRegisteredDecoders(availableDecoders.getTextStreamDecoders(clazz)); + metadata.setSinkClass(DecodedTextStreamMessageSink.class); } + else + { + throw new RuntimeException("Unable to add " + handler.getClass().getName() + ": type " + clazz + " is unrecognized by declared decoders"); + } + + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } private void assertBasicTypeNotRegistered(byte basicWebSocketType, MessageHandler replacement) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java index 80e1158f2abd..62a1b5c0a53f 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java @@ -52,25 +52,11 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteBufferMessageSink; import org.eclipse.jetty.websocket.core.messages.PartialStringMessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.eclipse.jetty.websocket.core.util.ReflectUtils; public abstract class JakartaWebSocketFrameHandlerFactory { - private static final MethodHandle FILTER_RETURN_TYPE_METHOD; - - static - { - try - { - FILTER_RETURN_TYPE_METHOD = getServerMethodHandleLookup() - .findVirtual(JakartaWebSocketSession.class, "filterReturnType", MethodType.methodType(void.class, Object.class)); - } - catch (Throwable e) - { - throw new RuntimeException(e); - } - } - static InvokerUtils.Arg[] getArgsFor(Class objectType) { return new InvokerUtils.Arg[]{new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(objectType).required()}; @@ -135,10 +121,10 @@ public JakartaWebSocketFrameHandler newJakartaWebSocketFrameHandler(Object endpo if (metadata == null) return null; - MethodHandle openHandle = metadata.getOpenHandle(); - MethodHandle closeHandle = metadata.getCloseHandle(); - MethodHandle errorHandle = metadata.getErrorHandle(); - MethodHandle pongHandle = metadata.getPongHandle(); + MethodHolder openHandle = MethodHolder.from(metadata.getOpenHandle()); + MethodHolder closeHandle = MethodHolder.from(metadata.getCloseHandle()); + MethodHolder errorHandle = MethodHolder.from(metadata.getErrorHandle()); + MethodHolder pongHandle = MethodHolder.from(metadata.getPongHandle()); JakartaWebSocketMessageMetadata textMetadata = JakartaWebSocketMessageMetadata.copyOf(metadata.getTextMetadata()); JakartaWebSocketMessageMetadata binaryMetadata = JakartaWebSocketMessageMetadata.copyOf(metadata.getBinaryMetadata()); @@ -156,9 +142,9 @@ public JakartaWebSocketFrameHandler newJakartaWebSocketFrameHandler(Object endpo pongHandle = bindTemplateVariables(pongHandle, namedVariables, pathParams); if (textMetadata != null) - textMetadata.setMethodHandle(bindTemplateVariables(textMetadata.getMethodHandle(), namedVariables, pathParams)); + textMetadata.setMethodHolder(bindTemplateVariables(textMetadata.getMethodHolder(), namedVariables, pathParams)); if (binaryMetadata != null) - binaryMetadata.setMethodHandle(bindTemplateVariables(binaryMetadata.getMethodHandle(), namedVariables, pathParams)); + binaryMetadata.setMethodHolder(bindTemplateVariables(binaryMetadata.getMethodHolder(), namedVariables, pathParams)); } openHandle = InvokerUtils.bindTo(openHandle, endpoint); @@ -190,15 +176,15 @@ public static MessageSink createMessageSink(JakartaWebSocketSession session, Jak if (AbstractDecodedMessageSink.class.isAssignableFrom(msgMetadata.getSinkClass())) { MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), - MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, List.class)); + MethodType.methodType(void.class, CoreSession.class, MethodHolder.class, List.class)); List registeredDecoders = msgMetadata.getRegisteredDecoders(); - return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle(), registeredDecoders); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), registeredDecoders); } else { MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), - MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, boolean.class)); - return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle(), true); + MethodType.methodType(void.class, CoreSession.class, MethodHolder.class)); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } catch (NoSuchMethodException e) @@ -219,23 +205,19 @@ public static MessageSink createMessageSink(JakartaWebSocketSession session, Jak } } - public static MethodHandle wrapNonVoidReturnType(MethodHandle handle, JakartaWebSocketSession session) + static MethodHolder wrapNonVoidReturnType(MethodHolder handle, JakartaWebSocketSession session) { if (handle == null) return null; - if (handle.type().returnType() == Void.TYPE) + if (handle.returnType() == Void.TYPE) return handle; - // Technique from https://stackoverflow.com/questions/48505787/methodhandle-with-general-non-void-return-filter - - // Change the return type of the to be Object so it will match exact with JakartaWebSocketSession.filterReturnType(Object) - handle = handle.asType(handle.type().changeReturnType(Object.class)); - - // Filter the method return type to a call to JakartaWebSocketSession.filterReturnType() bound to this session - handle = MethodHandles.filterReturnValue(handle, FILTER_RETURN_TYPE_METHOD.bindTo(session)); - - return handle; + return args -> + { + session.filterReturnType(handle.invoke(args)); + return null; + }; } private MethodHandle toMethodHandle(MethodHandles.Lookup lookup, Method method) @@ -252,7 +234,7 @@ private MethodHandle toMethodHandle(MethodHandles.Lookup lookup, Method method) protected JakartaWebSocketFrameHandlerMetadata createEndpointMetadata(EndpointConfig endpointConfig) { - JakartaWebSocketFrameHandlerMetadata metadata = new JakartaWebSocketFrameHandlerMetadata(endpointConfig, container.getWebSocketComponents()); + JakartaWebSocketFrameHandlerMetadata metadata = new JakartaWebSocketFrameHandlerMetadata(endpointConfig, components); MethodHandles.Lookup lookup = getServerMethodHandleLookup(); Method openMethod = ReflectUtils.findMethod(Endpoint.class, "onOpen", Session.class, EndpointConfig.class); @@ -360,7 +342,7 @@ private boolean matchOnMessage(Method onMsg, JakartaWebSocketFrameHandlerMetadat if (methodHandle != null) { msgMetadata.setSinkClass(PartialStringMessageSink.class); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); metadata.setTextMetadata(msgMetadata, onMsg); return true; } @@ -370,7 +352,7 @@ private boolean matchOnMessage(Method onMsg, JakartaWebSocketFrameHandlerMetadat if (methodHandle != null) { msgMetadata.setSinkClass(PartialByteBufferMessageSink.class); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } @@ -380,7 +362,7 @@ private boolean matchOnMessage(Method onMsg, JakartaWebSocketFrameHandlerMetadat if (methodHandle != null) { msgMetadata.setSinkClass(PartialByteArrayMessageSink.class); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); metadata.setBinaryMetadata(msgMetadata, onMsg); return true; } @@ -423,7 +405,7 @@ private boolean matchDecoders(Method onMsg, JakartaWebSocketFrameHandlerMetadata objectType = decoder.objectType; } MethodHandle methodHandle = getMethodHandle.apply(getArgsFor(objectType)); - msgMetadata.setMethodHandle(methodHandle); + msgMetadata.setMethodHolder(MethodHolder.from(methodHandle)); // Set the sinkClass and then set the MessageMetadata on the FrameHandlerMetadata if (interfaceType.equals(Decoder.Text.class)) @@ -508,7 +490,7 @@ private void assertSignatureValid(Class endpointClass, Method method, Class templateValues) + public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map templateValues) { if (target == null) { @@ -517,7 +499,7 @@ public static MethodHandle bindTemplateVariables(MethodHandle target, String[] n final int IDX = 1; - MethodHandle retHandle = target; + MethodHolder retHandle = target; if ((templateValues == null) || (templateValues.isEmpty())) { @@ -527,54 +509,54 @@ public static MethodHandle bindTemplateVariables(MethodHandle target, String[] n for (String variableName : namedVariables) { String strValue = templateValues.get(variableName); - Class type = retHandle.type().parameterType(IDX); + Class type = retHandle.parameterType(IDX); try { if (String.class.isAssignableFrom(type)) { - retHandle = MethodHandles.insertArguments(retHandle, IDX, strValue); + retHandle = retHandle.bindTo(strValue, IDX); } else if (Integer.class.isAssignableFrom(type) || Integer.TYPE.isAssignableFrom(type)) { Integer intValue = Integer.parseInt(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, intValue); + retHandle = retHandle.bindTo(intValue, IDX); } else if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) { Long longValue = Long.parseLong(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, longValue); + retHandle = retHandle.bindTo(longValue, IDX); } else if (Short.class.isAssignableFrom(type) || Short.TYPE.isAssignableFrom(type)) { Short shortValue = Short.parseShort(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, shortValue); + retHandle = retHandle.bindTo(shortValue, IDX); } else if (Float.class.isAssignableFrom(type) || Float.TYPE.isAssignableFrom(type)) { Float floatValue = Float.parseFloat(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, floatValue); + retHandle = retHandle.bindTo(floatValue, IDX); } else if (Double.class.isAssignableFrom(type) || Double.TYPE.isAssignableFrom(type)) { Double doubleValue = Double.parseDouble(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, doubleValue); + retHandle = retHandle.bindTo(doubleValue, IDX); } else if (Boolean.class.isAssignableFrom(type) || Boolean.TYPE.isAssignableFrom(type)) { Boolean boolValue = Boolean.parseBoolean(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, boolValue); + retHandle = retHandle.bindTo(boolValue, IDX); } else if (Character.class.isAssignableFrom(type) || Character.TYPE.isAssignableFrom(type)) { if (strValue.length() != 1) throw new IllegalArgumentException("Invalid Size"); Character charValue = strValue.charAt(0); - retHandle = MethodHandles.insertArguments(retHandle, IDX, charValue); + retHandle = retHandle.bindTo(charValue, IDX); } else if (Byte.class.isAssignableFrom(type) || Byte.TYPE.isAssignableFrom(type)) { Byte b = Byte.parseByte(strValue); - retHandle = MethodHandles.insertArguments(retHandle, IDX, b); + retHandle = retHandle.bindTo(b, IDX); } else { diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java index 891c56e22c0c..480ec3e89aa5 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java @@ -13,15 +13,15 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common; -import java.lang.invoke.MethodHandle; import java.util.List; import org.eclipse.jetty.ee9.websocket.jakarta.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class JakartaWebSocketMessageMetadata { - private MethodHandle methodHandle; + private MethodHolder methodHolder; private Class sinkClass; private List registeredDecoders; @@ -34,7 +34,7 @@ public static JakartaWebSocketMessageMetadata copyOf(JakartaWebSocketMessageMeta return null; JakartaWebSocketMessageMetadata copy = new JakartaWebSocketMessageMetadata(); - copy.methodHandle = metadata.methodHandle; + copy.methodHolder = metadata.methodHolder; copy.sinkClass = metadata.sinkClass; copy.registeredDecoders = metadata.registeredDecoders; copy.maxMessageSize = metadata.maxMessageSize; @@ -58,14 +58,14 @@ public void setMaxMessageSize(int maxMessageSize) this.maxMessageSizeSet = true; } - public MethodHandle getMethodHandle() + public MethodHolder getMethodHolder() { - return methodHandle; + return methodHolder; } - public void setMethodHandle(MethodHandle methodHandle) + public void setMethodHolder(MethodHolder methodHolder) { - this.methodHandle = methodHandle; + this.methodHolder = methodHolder; } public Class getSinkClass() diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java index 94afefa5b982..194b08a9499e 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; import java.util.List; import java.util.stream.Collectors; @@ -25,6 +24,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,12 +32,12 @@ public abstract class AbstractDecodedMessageSink implements MessageSink { private static final Logger LOG = LoggerFactory.getLogger(AbstractDecodedMessageSink.class); - private final MethodHandle _methodHandle; + private final MethodHolder _methodHolder; private final MessageSink _messageSink; - public AbstractDecodedMessageSink(CoreSession coreSession, MethodHandle methodHandle) + public AbstractDecodedMessageSink(CoreSession coreSession, MethodHolder methodHolder) { - _methodHandle = methodHandle; + _methodHolder = methodHolder; try { @@ -58,7 +58,7 @@ void invoke(Object message) { try { - _methodHandle.invoke(message); + _methodHolder.invoke(message); } catch (Throwable t) { @@ -67,7 +67,7 @@ void invoke(Object message) } /** - * @return a message sink which will first decode the message then pass it to {@link #_methodHandle}. + * @return a message sink which will first decode the message then pass it to {@link #_methodHolder}. * @throws Exception for any error in creating the message sink. */ abstract MessageSink newMessageSink(CoreSession coreSession) throws Exception; @@ -90,9 +90,9 @@ public abstract static class Basic extends AbstractDecodedMes { protected final List _decoders; - public Basic(CoreSession coreSession, MethodHandle methodHandle, List decoders) + public Basic(CoreSession coreSession, MethodHolder methodHolder, List decoders) { - super(coreSession, methodHandle); + super(coreSession, methodHolder); if (decoders.isEmpty()) throw new IllegalArgumentException("Require at least one decoder for " + this.getClass()); _decoders = decoders.stream() @@ -105,9 +105,9 @@ public abstract static class Stream extends AbstractDecodedMe { protected final T _decoder; - public Stream(CoreSession coreSession, MethodHandle methodHandle, List decoders) + public Stream(CoreSession coreSession, MethodHolder methodHolder, List decoders) { - super(coreSession, methodHandle); + super(coreSession, methodHolder); if (decoders.size() != 1) throw new IllegalArgumentException("Require exactly one decoder for " + this.getClass()); _decoder = decoders.get(0).getInstance(); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java index 1eab5c76a87b..f08d165711ac 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java @@ -13,8 +13,7 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.nio.ByteBuffer; import java.util.List; @@ -27,6 +26,7 @@ import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.ByteBufferMessageSink; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,18 +34,23 @@ public class DecodedBinaryMessageSink extends AbstractDecodedMessageSink.Basi { private static final Logger LOG = LoggerFactory.getLogger(DecodedBinaryMessageSink.class); - public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedBinaryMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override MessageSink newMessageSink(CoreSession coreSession) throws Exception { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(DecodedBinaryMessageSink.class, "onWholeMessage", MethodType.methodType(void.class, ByteBuffer.class)) - .bindTo(this); - return new ByteBufferMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onWholeMessage((ByteBuffer)args[0]); + return null; + }; + + return new ByteBufferMessageSink(coreSession, methodHolder, true); } public void onWholeMessage(ByteBuffer wholeMessage) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java index d7d07ad4859f..3d9e41af5d8c 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java @@ -15,34 +15,38 @@ import java.io.IOException; import java.io.InputStream; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.util.List; import jakarta.websocket.CloseReason; import jakarta.websocket.DecodeException; import jakarta.websocket.Decoder; -import org.eclipse.jetty.ee9.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee9.websocket.jakarta.common.decoders.RegisteredDecoder; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.InputStreamMessageSink; import org.eclipse.jetty.websocket.core.messages.MessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class DecodedBinaryStreamMessageSink extends AbstractDecodedMessageSink.Stream> { - public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedBinaryStreamMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws Exception + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(DecodedBinaryStreamMessageSink.class, "onStreamStart", MethodType.methodType(void.class, InputStream.class)) - .bindTo(this); - return new InputStreamMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onStreamStart((InputStream)args[0]); + return null; + }; + + return new InputStreamMessageSink(coreSession, methodHolder, true); } public void onStreamStart(InputStream stream) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSink.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSink.java index e7091d4596ef..11cc2179a81a 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSink.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSink.java @@ -13,8 +13,7 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.util.List; import jakarta.websocket.CloseReason; @@ -26,6 +25,7 @@ import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.messages.StringMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,18 +33,23 @@ public class DecodedTextMessageSink extends AbstractDecodedMessageSink.Basic< { private static final Logger LOG = LoggerFactory.getLogger(DecodedTextMessageSink.class); - public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedTextMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws NoSuchMethodException, IllegalAccessException + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(getClass(), "onMessage", MethodType.methodType(void.class, String.class)) - .bindTo(this); - return new StringMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onMessage((String)args[0]); + return null; + }; + + return new StringMessageSink(coreSession, methodHolder, true); } public void onMessage(String wholeMessage) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java index 47993416cf15..1bd6fb540557 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java @@ -15,8 +15,7 @@ import java.io.IOException; import java.io.Reader; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; import java.util.List; import jakarta.websocket.CloseReason; @@ -28,21 +27,27 @@ import org.eclipse.jetty.websocket.core.exception.CloseException; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.messages.ReaderMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public class DecodedTextStreamMessageSink extends AbstractDecodedMessageSink.Stream> { - public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List decoders) + public DecodedTextStreamMessageSink(CoreSession session, MethodHolder methodHolder, List decoders) { - super(session, methodHandle, decoders); + super(session, methodHolder, decoders); } @Override - MessageSink newMessageSink(CoreSession coreSession) throws Exception + MessageSink newMessageSink(CoreSession coreSession) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(DecodedTextStreamMessageSink.class, "onStreamStart", MethodType.methodType(void.class, Reader.class)) - .bindTo(this); - return new ReaderMessageSink(coreSession, methodHandle, true); + MethodHolder methodHolder = args -> + { + if (args.length != 1) + throw new WrongMethodTypeException(String.format("Expected %s params but had %s", 1, args.length)); + onStreamStart((Reader)args[0]); + return null; + }; + + return new ReaderMessageSink(coreSession, methodHolder, true); } public void onStreamStart(Reader reader) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractMessageSinkTest.java index e9b3cf0b1865..dc245da8720b 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/AbstractMessageSinkTest.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.AbstractSessionTest; import org.eclipse.jetty.ee9.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee9.websocket.jakarta.common.decoders.RegisteredDecoder; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public abstract class AbstractMessageSinkTest extends AbstractSessionTest { @@ -43,7 +44,7 @@ else if (Decoder.BinaryStream.class.isAssignableFrom(clazz)) return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build(), components)); } - public MethodHandle getAcceptHandle(Consumer copy, Class type) + public MethodHolder getAcceptHandle(Consumer copy, Class type) { try { @@ -51,7 +52,7 @@ public MethodHandle getAcceptHandle(Consumer copy, Class type) String name = "accept"; MethodType methodType = MethodType.methodType(void.class, type); MethodHandle handle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(refc, name, methodType); - return handle.bindTo(copy); + return MethodHolder.from(handle.bindTo(copy)); } catch (NoSuchMethodException | IllegalAccessException e) { diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java index aaa05b69ccdc..fd9fc87b60d5 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -45,7 +46,7 @@ public void testCalendar1Frame() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); + MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders); @@ -67,7 +68,7 @@ public void testCalendar3Frames() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); + MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java index 47863ff32713..1a28c22ab53b 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -47,7 +48,7 @@ public void testCalendar1Frame() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); + MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); @@ -69,7 +70,7 @@ public void testCalendar3Frames() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class); + MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java index f4c08ab3172e..1d27c4eae7eb 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -45,7 +46,7 @@ public void testDate1Frame() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Date.class); + MethodHolder copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders); @@ -62,7 +63,7 @@ public void testDate3Frames() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Date.class); + MethodHolder copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedTextMessageSink sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java index 4dd8cd533ee6..a72fa7cbde46 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -48,7 +49,7 @@ public void testDate1Frame() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Date.class); + MethodHolder copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); @@ -65,7 +66,7 @@ public void testDate3Frames() throws Exception { CompletableFuture copyFuture = new CompletableFuture<>(); DecodedDateCopy copy = new DecodedDateCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Date.class); + MethodHolder copyHandle = getAcceptHandle(copy, Date.class); List decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class); DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java index 56ccec6089ac..cab219df5341 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.messages.InputStreamMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static java.nio.charset.StandardCharsets.UTF_8; @@ -46,7 +47,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest public void testInputStream1Message1Frame() throws InterruptedException, ExecutionException, TimeoutException { InputStreamCopy copy = new InputStreamCopy(); - MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); + MethodHolder copyHandle = getAcceptHandle(copy, InputStream.class); InputStreamMessageSink sink = new InputStreamMessageSink(AbstractSessionTest.session.getCoreSession(), copyHandle, true); FutureCallback finCallback = new FutureCallback(); @@ -63,7 +64,7 @@ public void testInputStream1Message1Frame() throws InterruptedException, Executi public void testInputStream2Messages2Frames() throws InterruptedException, ExecutionException, TimeoutException { InputStreamCopy copy = new InputStreamCopy(); - MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); + MethodHolder copyHandle = getAcceptHandle(copy, InputStream.class); InputStreamMessageSink sink = new InputStreamMessageSink(AbstractSessionTest.session.getCoreSession(), copyHandle, true); FutureCallback fin1Callback = new FutureCallback(); @@ -91,7 +92,7 @@ public void testInputStream2Messages2Frames() throws InterruptedException, Execu public void testInputStream1Message3Frames() throws InterruptedException, ExecutionException, TimeoutException { InputStreamCopy copy = new InputStreamCopy(); - MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); + MethodHolder copyHandle = getAcceptHandle(copy, InputStream.class); InputStreamMessageSink sink = new InputStreamMessageSink(AbstractSessionTest.session.getCoreSession(), copyHandle, true); FutureCallback callback1 = new FutureCallback(); @@ -113,7 +114,7 @@ public void testInputStream1Message3Frames() throws InterruptedException, Execut public void testInputStream1Message4FramesEmptyFin() throws InterruptedException, ExecutionException, TimeoutException { InputStreamCopy copy = new InputStreamCopy(); - MethodHandle copyHandle = getAcceptHandle(copy, InputStream.class); + MethodHolder copyHandle = getAcceptHandle(copy, InputStream.class); InputStreamMessageSink sink = new InputStreamMessageSink(AbstractSessionTest.session.getCoreSession(), copyHandle, true); FutureCallback callback1 = new FutureCallback(); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/ReaderMessageSinkTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/ReaderMessageSinkTest.java index 795422ecc4f3..be2910d3474f 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/ReaderMessageSinkTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/messages/ReaderMessageSinkTest.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.messages.ReaderMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -40,7 +41,7 @@ public void testReader1Frame() throws InterruptedException, ExecutionException, { CompletableFuture copyFuture = new CompletableFuture<>(); ReaderCopy copy = new ReaderCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Reader.class); + MethodHolder copyHandle = getAcceptHandle(copy, Reader.class); ReaderMessageSink sink = new ReaderMessageSink(session.getCoreSession(), copyHandle, true); FutureCallback finCallback = new FutureCallback(); @@ -56,7 +57,7 @@ public void testReader3Frames() throws InterruptedException, ExecutionException, { CompletableFuture copyFuture = new CompletableFuture<>(); ReaderCopy copy = new ReaderCopy(copyFuture); - MethodHandle copyHandle = getAcceptHandle(copy, Reader.class); + MethodHolder copyHandle = getAcceptHandle(copy, Reader.class); ReaderMessageSink sink = new ReaderMessageSink(session.getCoreSession(), copyHandle, true); FutureCallback callback1 = new FutureCallback(); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java index 8d8e43732858..fc2525e5d413 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.eclipse.jetty.websocket.core.util.ReflectUtils; import org.junit.jupiter.api.Test; @@ -57,6 +58,12 @@ public String onColorMessage(Session session, String message, @Name("color") Str private static MethodHandles.Lookup lookup = MethodHandles.lookup(); + private MethodHolder getMethodHolder(Method method, String[] namedVariables, InvokerUtils.Arg... args) + { + MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables, args); + return MethodHolder.from(methodHandle); + } + @Test public void testOnlyParamString() throws Throwable { @@ -70,21 +77,21 @@ public void testOnlyParamString() throws Throwable // Raw Calling Args - none specified // Get basic method handle (without a instance to call against) - this is what the metadata stores - MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables); + MethodHolder methodHolder = getMethodHolder(method, namedVariables); // Some point later an actual instance is needed, which has static named parameters Map templateValues = new HashMap<>(); templateValues.put("fruit", "pear"); // Bind the static values, in same order as declared - methodHandle = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHandle, namedVariables, templateValues); + methodHolder = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHolder, namedVariables, templateValues); // Assign an instance to call. Foo foo = new Foo(); - methodHandle = methodHandle.bindTo(foo); + methodHolder = methodHolder.bindTo(foo); // Call method against instance - String result = (String)methodHandle.invoke(); + String result = (String)methodHolder.invoke(); assertThat("Result", result, is("onFruit('pear')")); } @@ -99,21 +106,21 @@ public void testOnlyParamInt() throws Throwable }; // Get basic method handle (without a instance to call against) - this is what the metadata stores - MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables); + MethodHolder methodHolder = getMethodHolder(method, namedVariables); // Some point later an actual instance is needed, which has static named parameters Map templateValues = new HashMap<>(); templateValues.put("count", "2222"); // Bind the static values for the variables, in same order as the variables were declared - methodHandle = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHandle, namedVariables, templateValues); + methodHolder = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHolder, namedVariables, templateValues); // Assign an instance to call. Foo foo = new Foo(); - methodHandle = methodHandle.bindTo(foo); + methodHolder = methodHolder.bindTo(foo); // Call method against instance - String result = (String)methodHandle.invoke(); + String result = (String)methodHolder.invoke(); assertThat("Result", result, is("onCount(2222)")); } @@ -130,21 +137,21 @@ public void testLabeledParamStringInt() throws Throwable final InvokerUtils.Arg ARG_LABEL = new InvokerUtils.Arg(String.class).required(); // Get basic method handle (without a instance to call against) - this is what the metadata stores - MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, Foo.class, method, new NameParamIdentifier(), namedVariables, ARG_LABEL); + MethodHolder methodHolder = getMethodHolder(method, namedVariables, ARG_LABEL); // Some point later an actual instance is needed, which has static named parameters Map templateValues = new HashMap<>(); templateValues.put("count", "444"); // Bind the static values for the variables, in same order as the variables were declared - methodHandle = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHandle, namedVariables, templateValues); + methodHolder = JakartaWebSocketFrameHandlerFactory.bindTemplateVariables(methodHolder, namedVariables, templateValues); // Assign an instance to call. Foo foo = new Foo(); - methodHandle = methodHandle.bindTo(foo); + methodHolder = methodHolder.bindTo(foo); // Call method against instance - String result = (String)methodHandle.invoke("cherry"); + String result = (String)methodHolder.invoke("cherry"); assertThat("Result", result, is("onLabeledCount('cherry', 444)")); } } diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/coders/DecoderTextStreamTest.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/coders/DecoderTextStreamTest.java index eec22ab2f5b1..2d666bbae694 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/coders/DecoderTextStreamTest.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee9/websocket/jakarta/tests/coders/DecoderTextStreamTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.WebSocketComponents; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -80,7 +81,7 @@ public void testQuotesDecodedReaderMessageSink() throws Exception }); List decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class); - DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders); + DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(quoteHandle), decoders); List callbacks = new ArrayList<>(); FutureCallback finCallback = null; diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandler.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandler.java index 6acd91608a00..1e44a3eaf8e9 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandler.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandler.java @@ -13,9 +13,9 @@ package org.eclipse.jetty.ee9.websocket.common; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jetty.ee9.websocket.api.BatchMode; @@ -42,6 +42,7 @@ import org.eclipse.jetty.websocket.core.exception.WebSocketTimeoutException; import org.eclipse.jetty.websocket.core.messages.MessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,16 +62,16 @@ private enum SuspendState private final Object endpointInstance; private final BatchMode batchMode; private final AtomicBoolean closeNotified = new AtomicBoolean(); - private MethodHandle openHandle; - private MethodHandle closeHandle; - private MethodHandle errorHandle; - private MethodHandle textHandle; + private MethodHolder openHandle; + private MethodHolder closeHandle; + private MethodHolder errorHandle; + private MethodHolder textHandle; private final Class textSinkClass; - private MethodHandle binaryHandle; + private MethodHolder binaryHandle; private final Class binarySinkClass; - private MethodHandle frameHandle; - private MethodHandle pingHandle; - private MethodHandle pongHandle; + private MethodHolder frameHandle; + private MethodHolder pingHandle; + private MethodHolder pongHandle; private UpgradeRequest upgradeRequest; private UpgradeResponse upgradeResponse; @@ -85,12 +86,12 @@ private enum SuspendState public JettyWebSocketFrameHandler(WebSocketContainer container, Object endpointInstance, - MethodHandle openHandle, MethodHandle closeHandle, MethodHandle errorHandle, - MethodHandle textHandle, MethodHandle binaryHandle, + MethodHolder openHandle, MethodHolder closeHandle, MethodHolder errorHandle, + MethodHolder textHandle, MethodHolder binaryHandle, Class textSinkClass, Class binarySinkClass, - MethodHandle frameHandle, - MethodHandle pingHandle, MethodHandle pongHandle, + MethodHolder frameHandle, + MethodHolder pingHandle, MethodHolder pongHandle, BatchMode batchMode, Configuration.Customizer customizer) { @@ -163,15 +164,13 @@ public void onOpen(CoreSession coreSession, Callback callback) pingHandle = InvokerUtils.bindTo(pingHandle, session); pongHandle = InvokerUtils.bindTo(pongHandle, session); + Executor executor = coreSession.getWebSocketComponents().getExecutor(); if (textHandle != null) - textSink = JettyWebSocketFrameHandlerFactory.createMessageSink(textHandle, textSinkClass, session); - + textSink = JettyWebSocketFrameHandlerFactory.createMessageSink(textHandle, textSinkClass, executor, session); if (binaryHandle != null) - binarySink = JettyWebSocketFrameHandlerFactory.createMessageSink(binaryHandle, binarySinkClass, session); - + binarySink = JettyWebSocketFrameHandlerFactory.createMessageSink(binaryHandle, binarySinkClass, executor, session); if (openHandle != null) openHandle.invoke(); - if (session.isOpen()) container.notifySessionListeners((listener) -> listener.onWebSocketSessionOpened(session)); diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandlerFactory.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandlerFactory.java index 51040f628a6d..1b7cb920c856 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandlerFactory.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee9/websocket/common/JettyWebSocketFrameHandlerFactory.java @@ -27,6 +27,7 @@ import java.time.Duration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import org.eclipse.jetty.ee9.websocket.api.BatchMode; import org.eclipse.jetty.ee9.websocket.api.Frame; @@ -58,6 +59,7 @@ import org.eclipse.jetty.websocket.core.messages.ReaderMessageSink; import org.eclipse.jetty.websocket.core.messages.StringMessageSink; import org.eclipse.jetty.websocket.core.util.InvokerUtils; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.eclipse.jetty.websocket.core.util.ReflectUtils; /** @@ -165,16 +167,16 @@ public JettyWebSocketFrameHandler newJettyFrameHandler(Object endpointInstance) { JettyWebSocketFrameHandlerMetadata metadata = getMetadata(endpointInstance.getClass()); - final MethodHandle openHandle = InvokerUtils.bindTo(metadata.getOpenHandle(), endpointInstance); - final MethodHandle closeHandle = InvokerUtils.bindTo(metadata.getCloseHandle(), endpointInstance); - final MethodHandle errorHandle = InvokerUtils.bindTo(metadata.getErrorHandle(), endpointInstance); - final MethodHandle textHandle = InvokerUtils.bindTo(metadata.getTextHandle(), endpointInstance); - final MethodHandle binaryHandle = InvokerUtils.bindTo(metadata.getBinaryHandle(), endpointInstance); + final MethodHolder openHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getOpenHandle()), endpointInstance); + final MethodHolder closeHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getCloseHandle()), endpointInstance); + final MethodHolder errorHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getErrorHandle()), endpointInstance); + final MethodHolder textHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getTextHandle()), endpointInstance); + final MethodHolder binaryHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getBinaryHandle()), endpointInstance); final Class textSinkClass = metadata.getTextSink(); final Class binarySinkClass = metadata.getBinarySink(); - final MethodHandle frameHandle = InvokerUtils.bindTo(metadata.getFrameHandle(), endpointInstance); - final MethodHandle pingHandle = InvokerUtils.bindTo(metadata.getPingHandle(), endpointInstance); - final MethodHandle pongHandle = InvokerUtils.bindTo(metadata.getPongHandle(), endpointInstance); + final MethodHolder frameHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getFrameHandle()), endpointInstance); + final MethodHolder pingHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getPingHandle()), endpointInstance); + final MethodHolder pongHandle = InvokerUtils.bindTo(MethodHolder.from(metadata.getPongHandle()), endpointInstance); BatchMode batchMode = metadata.getBatchMode(); // Decorate the endpointInstance while we are still upgrading for access to things like HttpSession. @@ -191,7 +193,7 @@ public JettyWebSocketFrameHandler newJettyFrameHandler(Object endpointInstance) metadata); } - public static MessageSink createMessageSink(MethodHandle msgHandle, Class sinkClass, WebSocketSession session) + public static MessageSink createMessageSink(MethodHolder msgHandle, Class sinkClass, Executor executor, WebSocketSession session) { if (msgHandle == null) return null; @@ -202,7 +204,7 @@ public static MessageSink createMessageSink(MethodHandle msgHandle, Classorg.eclipse.jetty jetty-server + + org.eclipse.jetty.websocket + jetty-websocket-jetty-server + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-client + org.eclipse.jetty jetty-http diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java new file mode 100644 index 000000000000..b85397396165 --- /dev/null +++ b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.jmh; + +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class MetafactoryTest +{ + + @FunctionalInterface + public interface Test + { + Object get(); + } + + public static void main(String[] args) throws Throwable + { + + MethodHandles.Lookup caller = MethodHandles.lookup(); + MethodHandle methodHandle = caller.findStatic(MetafactoryTest.class, "print", MethodType.methodType(Object.class, String.class)); + MethodType invokedType = MethodType.methodType(Test.class); + CallSite site = LambdaMetafactory.metafactory(caller, + "get", + invokedType, + methodHandle.type(), + methodHandle, + methodHandle.type()); + MethodHandle factory = site.getTarget(); + Test r = (Test)factory.invoke(); + System.err.println(r.get()); + } + + private static Object print(String s) + { + return "hello world"; + } + +} \ No newline at end of file diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java new file mode 100644 index 000000000000..15af5dbb1dad --- /dev/null +++ b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java @@ -0,0 +1,169 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.jmh; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.websocket.core.util.BindingMethodHolder2; +import org.eclipse.jetty.websocket.core.util.LambdaMetafactoryMethodHolder; +import org.eclipse.jetty.websocket.core.util.MethodHolder; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +public class MethodHolderBenchmark +{ + private MethodHandle methodHandle; + private MethodHolder bindingMethodHolder; + private MethodHolder nonBindingMethodHolder; + private BindingMethodHolder2 methodHolderWithOptimisation; + private LambdaMetafactoryMethodHolder lambdaMetafactoryMethodHolder; + + public static void main(String[] args) throws Throwable + { + MethodType methodType = MethodType.methodType(void.class, Blackhole.class, String.class, String.class); + MethodHandle methodHandle = MethodHandles.lookup() + .findVirtual(MethodHolderBenchmark.class, "method87964376", methodType); + if (methodHandle == null) + throw new IllegalStateException(); + + LambdaMetafactoryMethodHolder lambdaMetafactoryMethodHolder = new LambdaMetafactoryMethodHolder(methodHandle, MethodHandles.lookup()); + lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(null); + lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(null); + System.err.println(lambdaMetafactoryMethodHolder); + } + + @Setup(Level.Trial) + public void setupTrial(Blackhole blackhole) throws Throwable + { + MethodType methodType = MethodType.methodType(void.class, Blackhole.class, String.class, String.class); + methodHandle = MethodHandles.lookup() + .findVirtual(MethodHolderBenchmark.class, "method87964376", methodType); + if (methodHandle == null) + throw new IllegalStateException(); + + bindingMethodHolder = MethodHolder.from(methodHandle, true); + bindingMethodHolder.bindTo(this); + bindingMethodHolder.bindTo(Objects.requireNonNull(blackhole)); + + nonBindingMethodHolder = MethodHolder.from(methodHandle, false); + nonBindingMethodHolder.bindTo(this); + nonBindingMethodHolder.bindTo(Objects.requireNonNull(blackhole)); + + methodHolderWithOptimisation = new BindingMethodHolder2(methodHandle); + methodHolderWithOptimisation = methodHolderWithOptimisation.bindTo(this); + methodHolderWithOptimisation = methodHolderWithOptimisation.bindTo(Objects.requireNonNull(blackhole)); + + optimisedMethodHandle = methodHolderWithOptimisation.getMethodHandler(); + + lambdaMetafactoryMethodHolder = new LambdaMetafactoryMethodHolder(methodHandle, MethodHandles.lookup()); + lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(this); + lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(Objects.requireNonNull(blackhole)); + + + methodHandle = methodHandle.bindTo(this); + methodHandle = methodHandle.bindTo(Objects.requireNonNull(blackhole)); + } + + private MethodHandle optimisedMethodHandle; + + public void method87964376(Blackhole blackhole, String a, String b) + { + blackhole.consume(a); + blackhole.consume(b); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void methodHandleTest() throws Throwable + { + methodHandle.invoke("test", "12"); + } + +// @Benchmark +// @BenchmarkMode({Mode.Throughput}) +// public void methodHandleWithArgumentsTest() throws Throwable +// { +// methodHandle.invokeWithArguments("test", "12"); +// } +// +// @Benchmark +// @BenchmarkMode({Mode.Throughput}) +// public void methodHandleWithArgumentsArrayTest() throws Throwable +// { +// methodHandle.invokeWithArguments(new Object[]{"test", "12"}); +// } + +// @Benchmark +// @BenchmarkMode({Mode.Throughput}) +// public void bindingMethodHolderTest() throws Throwable +// { +// bindingMethodHolder.invoke("test", "12"); +// } + +// @Benchmark +// @BenchmarkMode({Mode.Throughput}) +// public void nonBindingMethodHolderTest() throws Throwable +// { +// nonBindingMethodHolder.invoke("test", "12"); +// } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void methodHolderWithOptimisationTest() throws Throwable + { + methodHolderWithOptimisation.invoke("test", "12"); +// optimisedMethodHandle.invoke("test", "12"); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void lambdaMetafactoryTest() throws Throwable + { + lambdaMetafactoryMethodHolder.invoke("test", "12"); + } + +// public static void main(String[] args) throws RunnerException +// { +// Options opt = new OptionsBuilder() +// .include(MethodHolderBenchmark.class.getSimpleName()) +// .warmupIterations(5) +// .measurementIterations(10) +// .addProfiler(LinuxPerfAsmProfiler.class) +//// .addProfiler(GCProfiler.class) +// .forks(1) +// .threads(1) +// .build(); +// +// new Runner(opt).run(); +// } +} + + diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java new file mode 100644 index 000000000000..6b1bf9b64973 --- /dev/null +++ b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java @@ -0,0 +1,200 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.jmh; + +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.websocket.api.Callback; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +public class WebSocketBenchmark +{ + private Server _server; + private ServerConnector _connector; + private WebSocketClient _client; + + @Param({"BINDING", "NON_BINDING"}) + public static String methodHandleType; + + @Setup(Level.Trial) + public void setupTrial() throws Exception + { + int capacity; + + switch (methodHandleType) + { + case "BINDING": + System.setProperty("jetty.websocket.methodholder.binding", Boolean.TRUE.toString()); + break; + case "NON_BINDING": + System.setProperty("jetty.websocket.methodholder.binding", Boolean.FALSE.toString()); + break; + default: + throw new IllegalStateException("Unknown methodHandleType Parameter"); + } + + _server = new Server(); + _connector = new ServerConnector(_server); + _server.addConnector(_connector); + + ContextHandler contextHandler = new ContextHandler(); + WebSocketUpgradeHandler upgradeHandler = WebSocketUpgradeHandler.from(_server, contextHandler); + contextHandler.setHandler(upgradeHandler); + _server.setHandler(contextHandler); + + upgradeHandler.configure(container -> + container.addMapping("/", (req, resp, cb) -> new ServerSocket())); + _server.start(); + + _client = new WebSocketClient(); + _client.start(); + } + + @TearDown(Level.Trial) + public void stopTrial() throws Exception + { + _client.stop(); + _server.stop(); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void test() throws Exception + { + ClientSocket clientSocket = new ClientSocket(); + URI uri = URI.create("ws://localhost:" + _connector.getLocalPort()); + + Session session = _client.connect(clientSocket, uri).get(5, TimeUnit.SECONDS); + for (int i = 0; i < 1000; i++) + { + FutureCallback callback = new FutureCallback(); + session.sendText("fixed string", callback); + callback.block(); + } + + FutureCallback callback = new FutureCallback(); + session.sendText("close", callback); + callback.block(); + if (!clientSocket._closeLatch.await(5, TimeUnit.SECONDS)) + throw new IllegalStateException(); + } + + public static void main(String[] args) throws RunnerException + { + Options opt = new OptionsBuilder() + .include(WebSocketBenchmark.class.getSimpleName()) + .warmupIterations(10) + .measurementIterations(20) + //.addProfiler(GCProfiler.class) + .forks(1) + .threads(1) + .build(); + + new Runner(opt).run(); + } + + @WebSocket + public static class ServerSocket + { + @OnWebSocketOpen + public void onOpen(Session session) + { + } + + @OnWebSocketMessage + public void onMessage(Session session, String message) + { + if ("close".equals(message)) + session.close(); + } + } + + @WebSocket + public static class ClientSocket + { + public CountDownLatch _closeLatch = new CountDownLatch(1); + + @OnWebSocketOpen + public void onOpen(Session session) + { + } + + @OnWebSocketMessage + public void onMessage(Session session, String message) + { + } + + @OnWebSocketClose + public void onClose(Session session, int statusCode, String reason) + { + _closeLatch.countDown(); + } + } + + public static class FutureCallback extends org.eclipse.jetty.util.FutureCallback implements Callback + { + private static final Logger LOG = LoggerFactory.getLogger(FutureCallback.class); + + @Override + public void fail(Throwable cause) + { + if (LOG.isDebugEnabled()) + LOG.debug(".writeFailed", cause); + failed(cause); + } + + @Override + public void succeed() + { + if (LOG.isDebugEnabled()) + LOG.debug(".writeSuccess"); + succeeded(); + } + } +} + + From d3a270594b9cf98656d3a309eef79229937822f9 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 5 Oct 2023 21:43:21 +1100 Subject: [PATCH 2/6] remove signature validation from MessageSinks Signed-off-by: Lachlan Roberts --- .../core/messages/ByteArrayMessageSink.java | 9 --------- .../core/messages/ByteBufferMessageSink.java | 14 -------------- .../common/internal/ByteBufferMessageSink.java | 8 +------- .../internal/PartialByteBufferMessageSink.java | 10 ---------- 4 files changed, 1 insertion(+), 40 deletions(-) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java index be2b0a3c1314..c6d4b87518c7 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteArrayMessageSink.java @@ -13,8 +13,6 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.nio.ByteBuffer; import org.eclipse.jetty.io.ByteBufferCallbackAccumulator; @@ -22,7 +20,6 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.websocket.core.CoreSession; import org.eclipse.jetty.websocket.core.Frame; -import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.exception.MessageTooLargeException; import org.eclipse.jetty.websocket.core.util.MethodHolder; @@ -45,12 +42,6 @@ public class ByteArrayMessageSink extends AbstractMessageSink public ByteArrayMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { super(session, methodHolder, autoDemand); - - // TODO: This uses the offset length byte array signature not supported by jakarta websocket. - // The jakarta layer instead uses decoders for whole byte array messages instead of this message sink. - // MethodType onMessageType = MethodType.methodType(Void.TYPE, byte[].class, int.class, int.class); - // if (methodHolder.type().changeReturnType(void.class) != onMessageType.changeReturnType(void.class)) - // throw InvalidSignatureException.build(onMessageType, methodHolder.type()); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java index 9c0bc1523278..ad3c0534c943 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/messages/ByteBufferMessageSink.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import org.eclipse.jetty.io.ByteBufferCallbackAccumulator; @@ -42,21 +41,8 @@ public class ByteBufferMessageSink extends AbstractMessageSink * @param autoDemand whether this {@link MessageSink} manages demand automatically */ public ByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) - { - this(session, methodHolder, autoDemand, true); - } - - protected ByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand, boolean validateSignature) { super(session, methodHolder, autoDemand); - - if (validateSignature) - { - // TODO: fix - // MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class); - // if (methodHolder.type() != onMessageType) - // throw InvalidSignatureException.build(onMessageType, methodHandle.type()); - } } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java index 8ea5061b5d5e..7eadef8f20be 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java @@ -23,13 +23,7 @@ public class ByteBufferMessageSink extends org.eclipse.jetty.websocket.core.mess { public ByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHolder, autoDemand, false); - - /* TODO - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, Callback.class); - if (methodHolder.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHolder.type()); - */ + super(session, methodHolder, autoDemand); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java index 45a816946596..04a524e58fa7 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/PartialByteBufferMessageSink.java @@ -13,13 +13,10 @@ package org.eclipse.jetty.websocket.common.internal; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.nio.ByteBuffer; import org.eclipse.jetty.websocket.api.Callback; import org.eclipse.jetty.websocket.core.CoreSession; -import org.eclipse.jetty.websocket.core.exception.InvalidSignatureException; import org.eclipse.jetty.websocket.core.util.MethodHolder; public class PartialByteBufferMessageSink extends org.eclipse.jetty.websocket.core.messages.PartialByteBufferMessageSink @@ -27,13 +24,6 @@ public class PartialByteBufferMessageSink extends org.eclipse.jetty.websocket.co public PartialByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { super(session, methodHolder, autoDemand); - - /* - TODO: - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, boolean.class, Callback.class); - if (methodHolder.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHolder.type()); - */ } @Override From 4cab67736bb832a97154223be2f680d114fdf005 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 9 Oct 2023 16:32:40 +1100 Subject: [PATCH 3/6] fix MethodHolder doInvoke implementation Signed-off-by: Lachlan Roberts --- .../websocket/core/util/MethodHolder.java | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java index 8f0b8ee64692..e699dcaa7b41 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java @@ -76,32 +76,30 @@ static Object doInvoke(MethodHandle methodHandle, Object arg1, Object arg2) thro static Object doInvoke(MethodHandle methodHandle, Object... args) throws Throwable { - return methodHandle.invokeExact(args[0], args[1]); - -// switch (args.length) -// { -// case 0: -// return methodHandle.invoke(); -// case 1: -// return methodHandle.invoke(args[0]); -// case 2: -// return methodHandle.invoke(args[0], args[1]); -// case 3: -// return methodHandle.invoke(args[0], args[1], args[2]); -// case 4: -// return methodHandle.invoke(args[0], args[1], args[2], args[3]); -// case 5: -// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4]); -// case 6: -// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5]); -// case 7: -// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); -// case 8: -// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); -// case 9: -// return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); -// default: -// return methodHandle.invokeWithArguments(args); -// } + switch (args.length) + { + case 0: + return methodHandle.invoke(); + case 1: + return methodHandle.invoke(args[0]); + case 2: + return methodHandle.invoke(args[0], args[1]); + case 3: + return methodHandle.invoke(args[0], args[1], args[2]); + case 4: + return methodHandle.invoke(args[0], args[1], args[2], args[3]); + case 5: + return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4]); + case 6: + return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: + return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + case 8: + return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + case 9: + return methodHandle.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + default: + return methodHandle.invokeWithArguments(args); + } } } From 31d87e4f4dbb1aacddf4c9925cb8e5d6f3ef407b Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 16 Oct 2023 16:14:13 +1100 Subject: [PATCH 4/6] use MethodHolder in signature lookup of MessageSink constructor Signed-off-by: Lachlan Roberts --- .../jakarta/common/JakartaWebSocketFrameHandlerFactory.java | 4 ++-- .../jakarta/common/JakartaWebSocketFrameHandlerFactory.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java index e45a3a830dd7..afd8f383282d 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java @@ -191,14 +191,14 @@ public static MessageSink createMessageSink(JakartaWebSocketSession session, Jak if (AbstractDecodedMessageSink.class.isAssignableFrom(msgMetadata.getSinkClass())) { MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), - MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, List.class)); + MethodType.methodType(void.class, CoreSession.class, MethodHolder.class, List.class)); List registeredDecoders = msgMetadata.getRegisteredDecoders(); return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), registeredDecoders); } else { MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), - MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, boolean.class)); + MethodType.methodType(void.class, CoreSession.class, MethodHolder.class, boolean.class)); return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java index 62a1b5c0a53f..11022f83bd24 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee9/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java @@ -183,7 +183,7 @@ public static MessageSink createMessageSink(JakartaWebSocketSession session, Jak else { MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(), - MethodType.methodType(void.class, CoreSession.class, MethodHolder.class)); + MethodType.methodType(void.class, CoreSession.class, MethodHolder.class, boolean.class)); return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } From b6e3e8759f0870f1cbbdc0108e11535af18c38c3 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 18 Oct 2023 15:18:39 +1100 Subject: [PATCH 5/6] fix jetty-jmh pom file after merge Signed-off-by: Lachlan Roberts --- tests/jetty-jmh/pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/jetty-jmh/pom.xml b/tests/jetty-jmh/pom.xml index 9613cd8e6fd8..d820f76f7141 100644 --- a/tests/jetty-jmh/pom.xml +++ b/tests/jetty-jmh/pom.xml @@ -37,6 +37,14 @@ org.eclipse.jetty.toolchain jetty-test-helper + + org.eclipse.jetty.websocket + jetty-websocket-jetty-client + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-server + org.openjdk.jmh jmh-core @@ -120,12 +128,4 @@ - org.eclipse.jetty.websocket - jetty-websocket-jetty-server - - - org.eclipse.jetty.websocket - jetty-websocket-jetty-client - - From 132376f919a75b014d99bbe53952285600ab580b Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 7 Aug 2024 09:12:21 +1000 Subject: [PATCH 6/6] PR #10750 - cleanup websocket benchmarks Signed-off-by: Lachlan Roberts --- .../core/util/BindingMethodHolder.java | 12 +- .../core/util/BindingMethodHolder2.java | 69 ------ .../util/LambdaMetafactoryMethodHolder.java | 73 ------- .../jetty/websocket/jmh/MetafactoryTest.java | 53 ----- .../websocket/jmh/MethodHolderBenchmark.java | 120 +++-------- .../websocket/jmh/WebSocketBenchmark.java | 200 ------------------ 6 files changed, 42 insertions(+), 485 deletions(-) delete mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java delete mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java delete mode 100644 tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java delete mode 100644 tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java index 5f7553764fe2..b58f623a2158 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java @@ -28,7 +28,17 @@ public BindingMethodHolder(MethodHandle methodHandle) @Override public Object invoke(Object... args) throws Throwable { - return _methodHandle.invokeWithArguments(args); + return MethodHolder.doInvoke(_methodHandle, args); + } + + public MethodHandle getMethodHandler() + { + return _methodHandle; + } + + public Object invoke(Object o1, Object o2) throws Throwable + { + return MethodHolder.doInvoke(_methodHandle, o1, o2); } @Override diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java deleted file mode 100644 index 29eb9932859f..000000000000 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder2.java +++ /dev/null @@ -1,69 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.websocket.core.util; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; - -public class BindingMethodHolder2 implements MethodHolder -{ - public MethodHandle _methodHandle; - - public BindingMethodHolder2(MethodHandle methodHandle) - { - _methodHandle = methodHandle; - } - - @Override - public Object invoke(Object... args) throws Throwable - { - return MethodHolder.doInvoke(_methodHandle, args); - } - - public MethodHandle getMethodHandler() - { - return _methodHandle; - } - - public Object invoke(Object o1, Object o2) throws Throwable - { - return MethodHolder.doInvoke(_methodHandle, o1, o2); - } - - @Override - public BindingMethodHolder2 bindTo(Object arg) - { - _methodHandle = _methodHandle.bindTo(arg); - return this; - } - - @Override - public MethodHolder bindTo(Object arg, int idx) - { - _methodHandle = MethodHandles.insertArguments(_methodHandle, idx, arg); - return this; - } - - @Override - public Class parameterType(int idx) - { - return _methodHandle.type().parameterType(idx); - } - - @Override - public Class returnType() - { - return _methodHandle.type().returnType(); - } -} diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java deleted file mode 100644 index a5cd13a75be2..000000000000 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/LambdaMetafactoryMethodHolder.java +++ /dev/null @@ -1,73 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.websocket.core.util; - -import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.function.Supplier; - -public class LambdaMetafactoryMethodHolder implements MethodHolder -{ - private final CallSite _callSite; - - public LambdaMetafactoryMethodHolder(MethodHandle methodHandle, MethodHandles.Lookup lookup) throws Throwable - { - MethodType methodType = methodHandle.type().changeReturnType(Supplier.class); - _callSite = LambdaMetafactory.metafactory( - lookup, - "get", - methodType, - MethodType.methodType(Object.class), - methodHandle, - // Supplier method real signature (reified) - // trim accepts no parameters and returns String - MethodType.methodType(methodType.returnType())); - } - - @SuppressWarnings("unchecked") - @Override - public Object invoke(Object... args) throws Throwable - { - return ((Supplier)MethodHolder.doInvoke(_callSite.getTarget(), args)).get(); - } - - @Override - public LambdaMetafactoryMethodHolder bindTo(Object arg) - { - _callSite.setTarget(_callSite.getTarget().bindTo(arg)); - return this; - } - - @Override - public MethodHolder bindTo(Object arg, int idx) - { - _callSite.setTarget(MethodHandles.insertArguments(_callSite.getTarget(), idx, arg)); - return this; - } - - @Override - public Class parameterType(int idx) - { - return _callSite.type().parameterType(idx); - } - - @Override - public Class returnType() - { - return _callSite.type().returnType(); - } -} diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java deleted file mode 100644 index b85397396165..000000000000 --- a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MetafactoryTest.java +++ /dev/null @@ -1,53 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.websocket.jmh; - -import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -public class MetafactoryTest -{ - - @FunctionalInterface - public interface Test - { - Object get(); - } - - public static void main(String[] args) throws Throwable - { - - MethodHandles.Lookup caller = MethodHandles.lookup(); - MethodHandle methodHandle = caller.findStatic(MetafactoryTest.class, "print", MethodType.methodType(Object.class, String.class)); - MethodType invokedType = MethodType.methodType(Test.class); - CallSite site = LambdaMetafactory.metafactory(caller, - "get", - invokedType, - methodHandle.type(), - methodHandle, - methodHandle.type()); - MethodHandle factory = site.getTarget(); - Test r = (Test)factory.invoke(); - System.err.println(r.get()); - } - - private static Object print(String s) - { - return "hello world"; - } - -} \ No newline at end of file diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java index 15af5dbb1dad..c531fd4ed48c 100644 --- a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java +++ b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java @@ -19,8 +19,6 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.websocket.core.util.BindingMethodHolder2; -import org.eclipse.jetty.websocket.core.util.LambdaMetafactoryMethodHolder; import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -33,6 +31,10 @@ import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; @State(Scope.Benchmark) @Threads(4) @@ -41,60 +43,31 @@ public class MethodHolderBenchmark { private MethodHandle methodHandle; - private MethodHolder bindingMethodHolder; - private MethodHolder nonBindingMethodHolder; - private BindingMethodHolder2 methodHolderWithOptimisation; - private LambdaMetafactoryMethodHolder lambdaMetafactoryMethodHolder; - - public static void main(String[] args) throws Throwable - { - MethodType methodType = MethodType.methodType(void.class, Blackhole.class, String.class, String.class); - MethodHandle methodHandle = MethodHandles.lookup() - .findVirtual(MethodHolderBenchmark.class, "method87964376", methodType); - if (methodHandle == null) - throw new IllegalStateException(); - - LambdaMetafactoryMethodHolder lambdaMetafactoryMethodHolder = new LambdaMetafactoryMethodHolder(methodHandle, MethodHandles.lookup()); - lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(null); - lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(null); - System.err.println(lambdaMetafactoryMethodHolder); - } + private MethodHolder methodHolderNonBinding; + private MethodHolder methodHolderBinding; @Setup(Level.Trial) public void setupTrial(Blackhole blackhole) throws Throwable { MethodType methodType = MethodType.methodType(void.class, Blackhole.class, String.class, String.class); methodHandle = MethodHandles.lookup() - .findVirtual(MethodHolderBenchmark.class, "method87964376", methodType); + .findVirtual(MethodHolderBenchmark.class, "consume", methodType); if (methodHandle == null) throw new IllegalStateException(); - bindingMethodHolder = MethodHolder.from(methodHandle, true); - bindingMethodHolder.bindTo(this); - bindingMethodHolder.bindTo(Objects.requireNonNull(blackhole)); - - nonBindingMethodHolder = MethodHolder.from(methodHandle, false); - nonBindingMethodHolder.bindTo(this); - nonBindingMethodHolder.bindTo(Objects.requireNonNull(blackhole)); - - methodHolderWithOptimisation = new BindingMethodHolder2(methodHandle); - methodHolderWithOptimisation = methodHolderWithOptimisation.bindTo(this); - methodHolderWithOptimisation = methodHolderWithOptimisation.bindTo(Objects.requireNonNull(blackhole)); - - optimisedMethodHandle = methodHolderWithOptimisation.getMethodHandler(); - - lambdaMetafactoryMethodHolder = new LambdaMetafactoryMethodHolder(methodHandle, MethodHandles.lookup()); - lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(this); - lambdaMetafactoryMethodHolder = lambdaMetafactoryMethodHolder.bindTo(Objects.requireNonNull(blackhole)); + methodHolderBinding = MethodHolder.from(methodHandle, true); + methodHolderBinding.bindTo(this); + methodHolderBinding.bindTo(Objects.requireNonNull(blackhole)); + methodHolderNonBinding = MethodHolder.from(methodHandle, false); + methodHolderNonBinding.bindTo(this); + methodHolderNonBinding.bindTo(Objects.requireNonNull(blackhole)); methodHandle = methodHandle.bindTo(this); methodHandle = methodHandle.bindTo(Objects.requireNonNull(blackhole)); } - private MethodHandle optimisedMethodHandle; - - public void method87964376(Blackhole blackhole, String a, String b) + public void consume(Blackhole blackhole, String a, String b) { blackhole.consume(a); blackhole.consume(b); @@ -102,68 +75,37 @@ public void method87964376(Blackhole blackhole, String a, String b) @Benchmark @BenchmarkMode({Mode.Throughput}) - public void methodHandleTest() throws Throwable + public void methodHandle() throws Throwable { methodHandle.invoke("test", "12"); } -// @Benchmark -// @BenchmarkMode({Mode.Throughput}) -// public void methodHandleWithArgumentsTest() throws Throwable -// { -// methodHandle.invokeWithArguments("test", "12"); -// } -// -// @Benchmark -// @BenchmarkMode({Mode.Throughput}) -// public void methodHandleWithArgumentsArrayTest() throws Throwable -// { -// methodHandle.invokeWithArguments(new Object[]{"test", "12"}); -// } - -// @Benchmark -// @BenchmarkMode({Mode.Throughput}) -// public void bindingMethodHolderTest() throws Throwable -// { -// bindingMethodHolder.invoke("test", "12"); -// } - -// @Benchmark -// @BenchmarkMode({Mode.Throughput}) -// public void nonBindingMethodHolderTest() throws Throwable -// { -// nonBindingMethodHolder.invoke("test", "12"); -// } - @Benchmark @BenchmarkMode({Mode.Throughput}) - public void methodHolderWithOptimisationTest() throws Throwable + public void methodHolderNonBinding() throws Throwable { - methodHolderWithOptimisation.invoke("test", "12"); -// optimisedMethodHandle.invoke("test", "12"); + methodHolderNonBinding.invoke("test", "12"); } @Benchmark @BenchmarkMode({Mode.Throughput}) - public void lambdaMetafactoryTest() throws Throwable + public void methodHolderBinding() throws Throwable { - lambdaMetafactoryMethodHolder.invoke("test", "12"); + methodHolderBinding.invoke("test", "12"); } -// public static void main(String[] args) throws RunnerException -// { -// Options opt = new OptionsBuilder() -// .include(MethodHolderBenchmark.class.getSimpleName()) -// .warmupIterations(5) -// .measurementIterations(10) -// .addProfiler(LinuxPerfAsmProfiler.class) -//// .addProfiler(GCProfiler.class) -// .forks(1) -// .threads(1) -// .build(); -// -// new Runner(opt).run(); -// } + public static void main(String[] args) throws RunnerException + { + Options opt = new OptionsBuilder() + .include(MethodHolderBenchmark.class.getSimpleName()) + .warmupIterations(5) + .measurementIterations(10) + .forks(1) + .threads(1) + .build(); + + new Runner(opt).run(); + } } diff --git a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java deleted file mode 100644 index 6b1bf9b64973..000000000000 --- a/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/WebSocketBenchmark.java +++ /dev/null @@ -1,200 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.websocket.jmh; - -import java.net.URI; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.websocket.api.Callback; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; -import org.eclipse.jetty.websocket.client.WebSocketClient; -import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@State(Scope.Benchmark) -@Threads(4) -@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) -public class WebSocketBenchmark -{ - private Server _server; - private ServerConnector _connector; - private WebSocketClient _client; - - @Param({"BINDING", "NON_BINDING"}) - public static String methodHandleType; - - @Setup(Level.Trial) - public void setupTrial() throws Exception - { - int capacity; - - switch (methodHandleType) - { - case "BINDING": - System.setProperty("jetty.websocket.methodholder.binding", Boolean.TRUE.toString()); - break; - case "NON_BINDING": - System.setProperty("jetty.websocket.methodholder.binding", Boolean.FALSE.toString()); - break; - default: - throw new IllegalStateException("Unknown methodHandleType Parameter"); - } - - _server = new Server(); - _connector = new ServerConnector(_server); - _server.addConnector(_connector); - - ContextHandler contextHandler = new ContextHandler(); - WebSocketUpgradeHandler upgradeHandler = WebSocketUpgradeHandler.from(_server, contextHandler); - contextHandler.setHandler(upgradeHandler); - _server.setHandler(contextHandler); - - upgradeHandler.configure(container -> - container.addMapping("/", (req, resp, cb) -> new ServerSocket())); - _server.start(); - - _client = new WebSocketClient(); - _client.start(); - } - - @TearDown(Level.Trial) - public void stopTrial() throws Exception - { - _client.stop(); - _server.stop(); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public void test() throws Exception - { - ClientSocket clientSocket = new ClientSocket(); - URI uri = URI.create("ws://localhost:" + _connector.getLocalPort()); - - Session session = _client.connect(clientSocket, uri).get(5, TimeUnit.SECONDS); - for (int i = 0; i < 1000; i++) - { - FutureCallback callback = new FutureCallback(); - session.sendText("fixed string", callback); - callback.block(); - } - - FutureCallback callback = new FutureCallback(); - session.sendText("close", callback); - callback.block(); - if (!clientSocket._closeLatch.await(5, TimeUnit.SECONDS)) - throw new IllegalStateException(); - } - - public static void main(String[] args) throws RunnerException - { - Options opt = new OptionsBuilder() - .include(WebSocketBenchmark.class.getSimpleName()) - .warmupIterations(10) - .measurementIterations(20) - //.addProfiler(GCProfiler.class) - .forks(1) - .threads(1) - .build(); - - new Runner(opt).run(); - } - - @WebSocket - public static class ServerSocket - { - @OnWebSocketOpen - public void onOpen(Session session) - { - } - - @OnWebSocketMessage - public void onMessage(Session session, String message) - { - if ("close".equals(message)) - session.close(); - } - } - - @WebSocket - public static class ClientSocket - { - public CountDownLatch _closeLatch = new CountDownLatch(1); - - @OnWebSocketOpen - public void onOpen(Session session) - { - } - - @OnWebSocketMessage - public void onMessage(Session session, String message) - { - } - - @OnWebSocketClose - public void onClose(Session session, int statusCode, String reason) - { - _closeLatch.countDown(); - } - } - - public static class FutureCallback extends org.eclipse.jetty.util.FutureCallback implements Callback - { - private static final Logger LOG = LoggerFactory.getLogger(FutureCallback.class); - - @Override - public void fail(Throwable cause) - { - if (LOG.isDebugEnabled()) - LOG.debug(".writeFailed", cause); - failed(cause); - } - - @Override - public void succeed() - { - if (LOG.isDebugEnabled()) - LOG.debug(".writeSuccess"); - succeeded(); - } - } -} - -