From e11120c3d1d2c9e7ae07111a33f9e07226c5a437 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 21 Aug 2024 10:51:16 +1000 Subject: [PATCH 1/3] Issue #6328 - avoid binding WebSocket MethodHandles Signed-off-by: Lachlan Roberts --- .../core/messages/AbstractMessageSink.java | 12 +- .../core/messages/ByteArrayMessageSink.java | 19 +- .../core/messages/ByteBufferMessageSink.java | 30 +-- .../core/messages/DispatchedMessageSink.java | 8 +- .../core/messages/InputStreamMessageSink.java | 7 +- .../messages/PartialByteArrayMessageSink.java | 11 +- .../PartialByteBufferMessageSink.java | 14 +- .../messages/PartialStringMessageSink.java | 13 +- .../core/messages/ReaderMessageSink.java | 7 +- .../core/messages/StringMessageSink.java | 11 +- .../core/util/BindingMethodHolder.java | 69 ++++++ .../websocket/core/util/InvokerUtils.java | 12 +- .../websocket/core/util/MethodHolder.java | 105 +++++++++ .../core/util/NonBindingMethodHolder.java | 103 +++++++++ .../util/PartialStringMessageSinkTest.java | 2 +- .../core/util/StringMessageSinkTest.java | 14 +- .../common/JettyWebSocketFrameHandler.java | 37 ++-- .../internal/ByteBufferMessageSink.java | 16 +- .../PartialByteBufferMessageSink.java | 16 +- .../common/OutgoingMessageCapture.java | 5 +- .../JakartaMessagePartialMethodHolder.java | 59 +++++ .../JakartaMessageWholeMethodHolder.java | 57 +++++ .../common/JakartaWebSocketFrameHandler.java | 205 ++++++++---------- .../JakartaWebSocketFrameHandlerFactory.java | 71 +++--- .../JakartaWebSocketMessageMetadata.java | 14 +- .../messages/AbstractDecodedMessageSink.java | 20 +- .../messages/DecodedBinaryMessageSink.java | 24 +- .../DecodedBinaryStreamMessageSink.java | 24 +- .../messages/DecodedTextMessageSink.java | 24 +- .../DecodedTextStreamMessageSink.java | 24 +- .../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 +- .../JakartaMessagePartialMethodHolder.java | 59 +++++ .../JakartaMessageWholeMethodHolder.java | 57 +++++ .../common/JakartaWebSocketFrameHandler.java | 201 ++++++++--------- .../JakartaWebSocketFrameHandlerFactory.java | 79 +++---- .../JakartaWebSocketMessageMetadata.java | 14 +- .../messages/AbstractDecodedMessageSink.java | 20 +- .../messages/DecodedBinaryMessageSink.java | 24 +- .../DecodedBinaryStreamMessageSink.java | 24 +- .../messages/DecodedTextMessageSink.java | 24 +- .../DecodedTextStreamMessageSink.java | 22 +- .../messages/AbstractMessageSinkTest.java | 5 +- .../DecodedBinaryMessageSinkTest.java | 6 +- .../DecodedBinaryStreamMessageSinkTest.java | 6 +- .../messages/DecodedTextMessageSinkTest.java | 6 +- .../DecodedTextStreamMessageSinkTest.java | 6 +- .../messages/InputStreamMessageSinkTest.java | 10 +- .../messages/ReaderMessageSinkTest.java | 6 +- .../util/InvokerUtilsStaticParamsTest.java | 32 +-- .../tests/coders/DecoderTextStreamTest.java | 3 +- .../JakartaMessagePartialMethodHolder.java | 59 +++++ .../JakartaMessageWholeMethodHolder.java | 57 +++++ .../common/JakartaWebSocketFrameHandler.java | 203 ++++++++--------- .../JakartaWebSocketFrameHandlerFactory.java | 88 +++----- .../JakartaWebSocketMessageMetadata.java | 14 +- .../messages/AbstractDecodedMessageSink.java | 20 +- .../messages/DecodedBinaryMessageSink.java | 22 +- .../DecodedBinaryStreamMessageSink.java | 24 +- .../messages/DecodedTextMessageSink.java | 24 +- .../DecodedTextStreamMessageSink.java | 24 +- .../messages/AbstractMessageSinkTest.java | 5 +- .../DecodedBinaryMessageSinkTest.java | 6 +- .../DecodedBinaryStreamMessageSinkTest.java | 6 +- .../messages/DecodedTextMessageSinkTest.java | 6 +- .../DecodedTextStreamMessageSinkTest.java | 6 +- .../messages/InputStreamMessageSinkTest.java | 10 +- .../messages/ReaderMessageSinkTest.java | 6 +- .../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 + .../websocket/jmh/MethodHolderBenchmark.java | 111 ++++++++++ 81 files changed, 1591 insertions(+), 909 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/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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java create mode 100644 jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/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/MethodHolderBenchmark.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 fa6349e1f11..979098405bd 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 @@ import org.eclipse.jetty.websocket.core.CoreSession; 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 abstract class AbstractMessageSink implements MessageSink * 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 2ee097d032a..ee77a8dba81 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.RetainableByteBuffer; @@ -22,8 +20,8 @@ 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.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,17 +36,12 @@ 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); - // 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()); + super(session, methodHolder, autoDemand); } @Override @@ -70,7 +63,7 @@ public class ByteArrayMessageSink extends AbstractMessageSink if (frame.isFin() && (accumulator == null || accumulator.isEmpty())) { byte[] buf = BufferUtil.toArray(payload); - getMethodHandle().invoke(buf, 0, buf.length); + getMethodHolder().invoke(buf, 0, buf.length); callback.succeeded(); autoDemand(); return; @@ -93,7 +86,7 @@ public class ByteArrayMessageSink extends AbstractMessageSink callback = Callback.NOOP; int length = accumulator.remaining(); byte[] buf = accumulator.takeByteArray(); - getMethodHandle().invoke(buf, 0, length); + getMethodHolder().invoke(buf, 0, 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 2783387b338..41d1df5210f 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,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; @@ -23,8 +21,8 @@ import org.eclipse.jetty.io.RetainableByteBuffer; 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,24 +37,12 @@ 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); - } - - protected ByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand, boolean validateSignature) - { - super(session, methodHandle, autoDemand); - - if (validateSignature) - { - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class); - if (methodHandle.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); - } + super(session, methodHolder, autoDemand); } @Override @@ -76,7 +62,7 @@ public class ByteBufferMessageSink extends AbstractMessageSink // created or used, then we don't need to aggregate. if (frame.isFin() && (accumulator == null || accumulator.getLength() == 0)) { - invoke(getMethodHandle(), frame.getPayload(), callback); + invoke(getMethodHolder(), frame.getPayload(), callback); autoDemand(); return; } @@ -99,7 +85,7 @@ public class ByteBufferMessageSink extends AbstractMessageSink ByteBuffer byteBuffer = buffer.getByteBuffer(); accumulator.writeTo(byteBuffer); callback = Callback.from(buffer::release); - invoke(getMethodHandle(), byteBuffer, callback); + invoke(getMethodHolder(), byteBuffer, callback); autoDemand(); } else @@ -122,9 +108,9 @@ public class ByteBufferMessageSink extends AbstractMessageSink 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 7deb4ed60d8..44ea17285b2 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 @@ -16,7 +16,6 @@ package org.eclipse.jetty.websocket.core.messages; import java.io.Closeable; import java.io.InputStream; import java.io.Reader; -import java.lang.invoke.MethodHandle; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.Executor; @@ -26,6 +25,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.websocket.core.CloseStatus; 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 @@ -51,9 +51,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(); @@ -74,7 +74,7 @@ public abstract class DispatchedMessageSink extends AbstractMessageSink { 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 72c65802387..bbf826747cb 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 c07b35484bc..37da9c08c2c 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 class PartialByteArrayMessageSink extends AbstractMessageSink 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 5cdf0ebba02..9fd7bf57beb 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 @@ -13,12 +13,12 @@ package org.eclipse.jetty.websocket.core.messages; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; 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 +31,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 +46,7 @@ public class PartialByteBufferMessageSink extends AbstractMessageSink { if (frame.hasPayload() || frame.isFin()) { - invoke(getMethodHandle(), frame.getPayload(), frame.isFin(), callback); + invoke(getMethodHolder(), frame.getPayload(), frame.isFin(), callback); autoDemand(); } else @@ -61,9 +61,9 @@ public class PartialByteBufferMessageSink extends AbstractMessageSink } } - 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 ba74c2c5f13..cf34986cf59 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 @@ -13,13 +13,12 @@ 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.util.MethodHolder; /** *

A {@link MessageSink} implementation that delivers TEXT frames @@ -34,12 +33,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 +54,12 @@ public class PartialStringMessageSink extends AbstractMessageSink 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 8f8e4f1759a..fb6cc87f1e7 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 e3bb1ded729..66fff19726e 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; } @@ -64,7 +63,7 @@ public class StringMessageSink extends AbstractMessageSink if (frame.isFin()) { - getMethodHandle().invoke(out.takeCompleteString(BadPayloadException.InvalidUtf8::new)); + getMethodHolder().invoke(out.takeCompleteString(BadPayloadException.InvalidUtf8::new)); reset(); 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 00000000000..b58f623a215 --- /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,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; + +class BindingMethodHolder implements MethodHolder +{ + public MethodHandle _methodHandle; + + public BindingMethodHolder(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 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/InvokerUtils.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/InvokerUtils.java index 6fc71474e63..e8a6e6c68e8 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 class InvokerUtils /** * 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/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 00000000000..e699dcaa7b4 --- /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,105 @@ +// +// ======================================================================== +// 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: + *

+ * + * 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 + { + 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 00000000000..b92fa4c6672 --- /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 80d6cd4302d..daa360a4822 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 a5448b74b45..69cbd26ece2 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 @@ import static org.junit.jupiter.api.Assertions.assertThrows; 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 class StringMessageSinkTest @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 class StringMessageSinkTest @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 class StringMessageSinkTest @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 class StringMessageSinkTest @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 d304ef864ad..902784fda5d 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.WebSocketException; 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 class JettyWebSocketFrameHandler implements FrameHandler 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) @@ -157,7 +158,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler } } - 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; @@ -168,7 +169,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler { 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/internal/ByteBufferMessageSink.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/internal/ByteBufferMessageSink.java index 4d9b6b3ef26..7eadef8f20b 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,22 @@ 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); - - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, Callback.class); - if (methodHandle.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); + super(session, methodHolder, autoDemand); } @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 479e90cb779..04a524e58fa 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,28 +13,22 @@ 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 { - public PartialByteBufferMessageSink(CoreSession session, MethodHandle methodHandle, boolean autoDemand) + public PartialByteBufferMessageSink(CoreSession session, MethodHolder methodHolder, boolean autoDemand) { - super(session, methodHandle, autoDemand); - - MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, boolean.class, Callback.class); - if (methodHandle.type() != onMessageType) - throw InvalidSignatureException.build(onMessageType, methodHandle.type()); + super(session, methodHolder, autoDemand); } @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 206eb603937..cb9edd2c9ad 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.Frame; 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 class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes 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 class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes 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 00000000000..7b3e66f1849 --- /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 00000000000..efe8bab30a8 --- /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 86f110b4ed6..a04015b94cf 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 @@ -13,8 +13,6 @@ package org.eclipse.jetty.ee10.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; @@ -51,6 +49,7 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteArrayMessageSink; 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 +61,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 +78,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 +146,10 @@ public class JakartaWebSocketFrameHandler implements FrameHandler 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; @@ -162,10 +161,10 @@ public class JakartaWebSocketFrameHandler implements FrameHandler 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 +345,88 @@ public class JakartaWebSocketFrameHandler implements FrameHandler 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)) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Partial.class, "onMessage", MethodType.methodType(void.class, Object.class, boolean.class)) - .bindTo(handler); + 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()); + } - 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); - } - catch (NoSuchMethodException e) - { - throw new IllegalStateException("Unable to find method", e); - } - catch (IllegalAccessException e) - { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); - } + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } public void addMessageHandler(Class clazz, MessageHandler.Whole handler) { - try + MethodHolder methodHolder = new JakartaMessageWholeMethodHolder<>(handler); + + if (PongMessage.class.isAssignableFrom(clazz)) { - 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; - } - - AvailableDecoders availableDecoders = session.getDecoders(); - RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); - if (registeredDecoder == null) - throw new IllegalStateException("Unable to find Decoder for type: " + clazz); - - // 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"); - } - - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + assertBasicTypeNotRegistered(OpCode.PONG, handler); + this.pongHandle = methodHolder; + registerMessageHandler(OpCode.PONG, clazz, handler, null); + return; } - catch (NoSuchMethodException e) + + AvailableDecoders availableDecoders = session.getDecoders(); + RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); + if (registeredDecoder == null) + throw new IllegalStateException("Unable to find Decoder for type: " + clazz); + + // Create the message metadata specific to the MessageHandler type. + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(methodHolder); + byte basicType; + if (registeredDecoder.implementsInterface(Decoder.Binary.class)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryMessageSink.class); } - catch (IllegalAccessException e) + else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + 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"); + } + + // 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 dd90ab38a13..afd8f383282 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.PartialByteArrayMessageSink; 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 abstract class JakartaWebSocketFrameHandlerFactory 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 abstract class JakartaWebSocketFrameHandlerFactory 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 +191,15 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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, boolean.class)); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } catch (NoSuchMethodException e) @@ -219,23 +220,19 @@ public abstract class JakartaWebSocketFrameHandlerFactory } } - 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory * have been statically assigned a converted value (and removed from the resulting {@link MethodHandle#type()}, or null if * no {@code target} MethodHandle was provided. */ - public static MethodHandle bindTemplateVariables(MethodHandle target, String[] namedVariables, Map templateValues) + public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map templateValues) { if (target == null) { @@ -517,7 +514,7 @@ public abstract class JakartaWebSocketFrameHandlerFactory final int IDX = 1; - MethodHandle retHandle = target; + MethodHolder retHandle = target; if ((templateValues == null) || (templateValues.isEmpty())) { @@ -527,54 +524,54 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 a5587ae30ae..7511f372e8c 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 class JakartaWebSocketMessageMetadata 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 class JakartaWebSocketMessageMetadata 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 a62d243fb89..5c24767ae58 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 @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee10.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.CoreSession; 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 @@ public abstract class AbstractDecodedMessageSink implements MessageSink { try { - _methodHandle.invoke(message); + _methodHolder.invoke(message); } catch (Throwable t) { @@ -67,7 +67,7 @@ public abstract class AbstractDecodedMessageSink implements MessageSink } /** - * @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 class AbstractDecodedMessageSink implements MessageSink { 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 class AbstractDecodedMessageSink implements MessageSink { 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 d3715baf894..28b7b8c1924 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 6e7b34c3ce0..ff699ae2c89 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 @@ -15,34 +15,38 @@ package org.eclipse.jetty.ee10.websocket.jakarta.common.messages; 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.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.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 6b9f98b6b13..a6db6771b77 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 59bed6e8b85..2c8d2909cae 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 @@ -15,34 +15,38 @@ package org.eclipse.jetty.ee10.websocket.jakarta.common.messages; 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; 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.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 4e21e76d317..a927fd560f2 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.ee10.websocket.jakarta.common.decoders.RegisteredDecode 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 class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest 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 class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest 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 19f2d82c00c..db830c3d2a1 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.BufferUtil; 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 class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest 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 class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest 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 e3fccf35041..eac4ee36f05 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.ee10.websocket.jakarta.common.decoders.RegisteredDecode 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 class DecodedTextMessageSinkTest extends AbstractMessageSinkTest 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 class DecodedTextMessageSinkTest extends AbstractMessageSinkTest 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 fe83857050c..9647c606886 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.FutureCallback; 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 class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest 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 class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest 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 b91f0e28702..3e08f41e03f 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.util.IO; 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 { 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 class InputStreamMessageSinkTest extends AbstractMessageSinkTest { 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 class InputStreamMessageSinkTest extends AbstractMessageSinkTest { 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 class InputStreamMessageSinkTest extends AbstractMessageSinkTest { 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 af3264e8d91..5cc83ebc99c 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.util.IO; 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 class ReaderMessageSinkTest extends AbstractMessageSinkTest 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 class ReaderMessageSinkTest extends AbstractMessageSinkTest 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 578a5ae83e6..6fd32c011a7 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 jakarta.websocket.Session; 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 class InvokerUtilsStaticParamsTest 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 class InvokerUtilsStaticParamsTest // 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 class InvokerUtilsStaticParamsTest }; // 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 class InvokerUtilsStaticParamsTest 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 73b8f3f3244..3e3f83780fb 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.toolchain.test.MavenTestingUtils; 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 class DecoderTextStreamTest extends AbstractClientSessionTest }); 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaMessagePartialMethodHolder.java new file mode 100644 index 00000000000..912b14989f7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/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.ee11.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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaMessageWholeMethodHolder.java new file mode 100644 index 00000000000..1dc900f343a --- /dev/null +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/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.ee11.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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandler.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandler.java index 5d868b33fbb..edb8777d31f 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandler.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandler.java @@ -13,8 +13,6 @@ package org.eclipse.jetty.ee11.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; @@ -51,6 +49,7 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteArrayMessageSink; 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 +61,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 +78,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 +146,10 @@ public class JakartaWebSocketFrameHandler implements FrameHandler 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; @@ -162,10 +161,10 @@ public class JakartaWebSocketFrameHandler implements FrameHandler if (actualBinaryMetadata.isMaxMessageSizeSet()) session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.getMaxMessageSize()); - MethodHandle methodHandle = actualBinaryMetadata.getMethodHandle(); + MethodHolder methodHandle = actualBinaryMetadata.getMethodHolder(); methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session); methodHandle = JakartaWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session); - actualBinaryMetadata.setMethodHandle(methodHandle); + actualBinaryMetadata.setMethodHolder(methodHandle); binarySink = JakartaWebSocketFrameHandlerFactory.createMessageSink(session, actualBinaryMetadata); binaryMetadata = actualBinaryMetadata; @@ -346,116 +345,88 @@ public class JakartaWebSocketFrameHandler implements FrameHandler 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)) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Partial.class, "onMessage", MethodType.methodType(void.class, Object.class, boolean.class)) - .bindTo(handler); + 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()); + } - 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); - } - catch (NoSuchMethodException e) - { - throw new IllegalStateException("Unable to find method", e); - } - catch (IllegalAccessException e) - { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); - } + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } public void addMessageHandler(Class clazz, MessageHandler.Whole handler) { - try + MethodHolder methodHolder = new JakartaMessageWholeMethodHolder<>(handler); + + if (PongMessage.class.isAssignableFrom(clazz)) { - 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; - } - - AvailableDecoders availableDecoders = session.getDecoders(); - RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); - if (registeredDecoder == null) - throw new IllegalStateException("Unable to find Decoder for type: " + clazz); - - // 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"); - } - - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + assertBasicTypeNotRegistered(OpCode.PONG, handler); + this.pongHandle = methodHolder; + registerMessageHandler(OpCode.PONG, clazz, handler, null); + return; } - catch (NoSuchMethodException e) + + AvailableDecoders availableDecoders = session.getDecoders(); + RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); + if (registeredDecoder == null) + throw new IllegalStateException("Unable to find Decoder for type: " + clazz); + + // Create the message metadata specific to the MessageHandler type. + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(methodHolder); + byte basicType; + if (registeredDecoder.implementsInterface(Decoder.Binary.class)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryMessageSink.class); } - catch (IllegalAccessException e) + else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + 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"); + } + + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } private void assertBasicTypeNotRegistered(byte basicWebSocketType, MessageHandler replacement) diff --git a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java index 95ca9ea7fa8..966fff0ba37 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.websocket.core.messages.PartialByteArrayMessageSink; 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 abstract class JakartaWebSocketFrameHandlerFactory 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 abstract class JakartaWebSocketFrameHandlerFactory 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 +191,15 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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, boolean.class)); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } catch (NoSuchMethodException e) @@ -219,23 +220,19 @@ public abstract class JakartaWebSocketFrameHandlerFactory } } - public static MethodHandle wrapNonVoidReturnType(MethodHandle handle, JakartaWebSocketSession session) + public static MethodHolder wrapNonVoidReturnType(MethodHolder holder, JakartaWebSocketSession session) { - if (handle == null) + if (holder == null) return null; - if (handle.type().returnType() == Void.TYPE) - return handle; + if (holder.returnType() == Void.TYPE) + return holder; - // 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(holder.invoke(args)); + return null; + }; } private MethodHandle toMethodHandle(MethodHandles.Lookup lookup, Method method) @@ -360,7 +357,7 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory * have been statically assigned a converted value (and removed from the resulting {@link MethodHandle#type()}, or null if * no {@code target} MethodHandle was provided. */ - public static MethodHandle bindTemplateVariables(MethodHandle target, String[] namedVariables, Map templateValues) + public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map templateValues) { if (target == null) { @@ -517,7 +514,7 @@ public abstract class JakartaWebSocketFrameHandlerFactory final int IDX = 1; - MethodHandle retHandle = target; + MethodHolder retHandle = target; if ((templateValues == null) || (templateValues.isEmpty())) { @@ -527,54 +524,58 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java index 80876d0e3a1..f26a7e82a92 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java @@ -13,15 +13,15 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common; -import java.lang.invoke.MethodHandle; import java.util.List; import org.eclipse.jetty.ee11.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 class JakartaWebSocketMessageMetadata 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 class JakartaWebSocketMessageMetadata 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java index 575d0489a7a..b30fdab99ac 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee11.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.CoreSession; 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 @@ public abstract class AbstractDecodedMessageSink implements MessageSink { try { - _methodHandle.invoke(message); + _methodHolder.invoke(message); } catch (Throwable t) { @@ -67,7 +67,7 @@ public abstract class AbstractDecodedMessageSink implements MessageSink } /** - * @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 class AbstractDecodedMessageSink implements MessageSink { 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 class AbstractDecodedMessageSink implements MessageSink { 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java index c280e40979e..b9f5fa0dbd2 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java @@ -13,20 +13,19 @@ package org.eclipse.jetty.ee11.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.ee11.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee11.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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java index 97e95c39b29..c3442e3b52d 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java @@ -15,34 +15,38 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; 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.ee11.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee11.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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSink.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSink.java index ccb18c696b1..53ad922af79 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSink.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSink.java @@ -13,19 +13,18 @@ package org.eclipse.jetty.ee11.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.ee11.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee11.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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java index 4872af80aea..04d64065f74 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java @@ -15,34 +15,38 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; 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; import jakarta.websocket.DecodeException; import jakarta.websocket.Decoder; -import org.eclipse.jetty.ee11.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee11.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.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 { - 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractMessageSinkTest.java index 30978d02820..48228de9072 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/AbstractMessageSinkTest.java @@ -23,6 +23,7 @@ import jakarta.websocket.Decoder; import org.eclipse.jetty.ee11.websocket.jakarta.common.AbstractSessionTest; import org.eclipse.jetty.ee11.websocket.jakarta.common.JakartaWebSocketFrameHandlerFactory; import org.eclipse.jetty.ee11.websocket.jakarta.common.decoders.RegisteredDecoder; +import org.eclipse.jetty.websocket.core.util.MethodHolder; public abstract class AbstractMessageSinkTest extends AbstractSessionTest { @@ -43,7 +44,7 @@ public abstract class AbstractMessageSinkTest extends AbstractSessionTest 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 abstract class AbstractMessageSinkTest extends AbstractSessionTest 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java index 48c1eb34fff..f7801e8a3f6 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -30,6 +29,7 @@ import org.eclipse.jetty.ee11.websocket.jakarta.common.decoders.RegisteredDecode 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; @@ -44,7 +44,7 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest { 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<>(session.getCoreSession(), copyHandle, decoders); @@ -67,7 +67,7 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest { 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<>(session.getCoreSession(), copyHandle, decoders); diff --git a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java index 4daf2e54372..51b39559bc4 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; import java.io.IOException; import java.io.InputStream; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -33,6 +32,7 @@ import org.eclipse.jetty.util.BufferUtil; 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 +47,7 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest { 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); @@ -70,7 +70,7 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest { 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java index 556bc498b37..ea5bb08be2b 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -31,6 +30,7 @@ import org.eclipse.jetty.ee11.websocket.jakarta.common.decoders.RegisteredDecode 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 +45,7 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest { 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); @@ -63,7 +63,7 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest { 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java index 916e1cb7651..30907de3d47 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; import java.io.IOException; import java.io.Reader; -import java.lang.invoke.MethodHandle; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -34,6 +33,7 @@ import org.eclipse.jetty.util.FutureCallback; 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 +48,7 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest { 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); @@ -66,7 +66,7 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest { 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java index 9614ef299d2..8c55c90c329 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -32,6 +31,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.messages.InputStreamMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static java.nio.charset.StandardCharsets.UTF_8; @@ -45,7 +45,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(session.getCoreSession(), copyHandle, true); FutureCallback finCallback = new FutureCallback(); @@ -63,7 +63,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest 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(session.getCoreSession(), copyHandle, true); FutureCallback fin1Callback = new FutureCallback(); @@ -94,7 +94,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest 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(session.getCoreSession(), copyHandle, true); FutureCallback callback1 = new FutureCallback(); @@ -119,7 +119,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest 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(session.getCoreSession(), copyHandle, true); FutureCallback callback1 = new FutureCallback(); diff --git a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/ReaderMessageSinkTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/ReaderMessageSinkTest.java index 44918fcd332..b33a4f12ab8 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/ReaderMessageSinkTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/messages/ReaderMessageSinkTest.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.ee11.websocket.jakarta.common.messages; import java.io.IOException; import java.io.Reader; import java.io.StringWriter; -import java.lang.invoke.MethodHandle; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -28,6 +27,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.messages.ReaderMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -40,7 +40,7 @@ public class ReaderMessageSinkTest extends AbstractMessageSinkTest { 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(); @@ -57,7 +57,7 @@ public class ReaderMessageSinkTest extends AbstractMessageSinkTest { 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java index d5970fa59db..daa86511342 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java @@ -23,6 +23,7 @@ import jakarta.websocket.Session; import org.eclipse.jetty.ee11.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 class InvokerUtilsStaticParamsTest 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,20 @@ public class InvokerUtilsStaticParamsTest // 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 +105,21 @@ public class InvokerUtilsStaticParamsTest }; // 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 +136,21 @@ public class InvokerUtilsStaticParamsTest 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-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/tests/coders/DecoderTextStreamTest.java b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/tests/coders/DecoderTextStreamTest.java index 3e47efb211d..8c7d9cffa06 100644 --- a/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/tests/coders/DecoderTextStreamTest.java +++ b/jetty-ee11/jetty-ee11-websocket/jetty-ee11-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee11/websocket/jakarta/tests/coders/DecoderTextStreamTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils; 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 class DecoderTextStreamTest extends AbstractClientSessionTest }); 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-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 00000000000..76ecd0effa9 --- /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 00000000000..73c0ce3ca13 --- /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 77fbcabffb6..edcc4aa61fc 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.PartialByteArrayMessageSink; 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 class JakartaWebSocketFrameHandler implements FrameHandler 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 class JakartaWebSocketFrameHandler implements FrameHandler 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 class JakartaWebSocketFrameHandler implements FrameHandler 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)) { - MethodHandle methodHandle = JakartaWebSocketFrameHandlerFactory.getServerMethodHandleLookup() - .findVirtual(MessageHandler.Partial.class, "onMessage", MethodType.methodType(void.class, Object.class, boolean.class)) - .bindTo(handler); + 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()); + } - 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); - } - catch (NoSuchMethodException e) - { - throw new IllegalStateException("Unable to find method", e); - } - catch (IllegalAccessException e) - { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); - } + // Register the Metadata as a MessageHandler. + registerMessageHandler(clazz, handler, basicType, metadata); } public void addMessageHandler(Class clazz, MessageHandler.Whole handler) { - try + MethodHolder methodHolder = new JakartaMessageWholeMethodHolder<>(handler); + + if (PongMessage.class.isAssignableFrom(clazz)) { - 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; - } - - AvailableDecoders availableDecoders = session.getDecoders(); - RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); - if (registeredDecoder == null) - throw new IllegalStateException("Unable to find Decoder for type: " + clazz); - - // 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"); - } - - // Register the Metadata as a MessageHandler. - registerMessageHandler(clazz, handler, basicType, metadata); + assertBasicTypeNotRegistered(OpCode.PONG, handler); + this.pongHandle = methodHolder; + registerMessageHandler(OpCode.PONG, clazz, handler, null); + return; } - catch (NoSuchMethodException e) + + AvailableDecoders availableDecoders = session.getDecoders(); + RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz); + if (registeredDecoder == null) + throw new IllegalStateException("Unable to find Decoder for type: " + clazz); + + // Create the message metadata specific to the MessageHandler type. + JakartaWebSocketMessageMetadata metadata = new JakartaWebSocketMessageMetadata(); + metadata.setMethodHolder(methodHolder); + byte basicType; + if (registeredDecoder.implementsInterface(Decoder.Binary.class)) { - throw new IllegalStateException("Unable to find method", e); + basicType = OpCode.BINARY; + metadata.setRegisteredDecoders(availableDecoders.getBinaryDecoders(clazz)); + metadata.setSinkClass(DecodedBinaryMessageSink.class); } - catch (IllegalAccessException e) + else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class)) { - throw new IllegalStateException("Unable to access " + handler.getClass().getName(), e); + 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"); + } + + // 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 80e1158f2ab..11022f83bd2 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.PartialByteArrayMessageSink; 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 abstract class JakartaWebSocketFrameHandlerFactory 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 abstract class JakartaWebSocketFrameHandlerFactory 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 abstract class JakartaWebSocketFrameHandlerFactory 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, boolean.class)); + return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHolder(), true); } } catch (NoSuchMethodException e) @@ -219,23 +205,19 @@ public abstract class JakartaWebSocketFrameHandlerFactory } } - 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 @@ public abstract class JakartaWebSocketFrameHandlerFactory * have been statically assigned a converted value (and removed from the resulting {@link MethodHandle#type()}, or null if * no {@code target} MethodHandle was provided. */ - public static MethodHandle bindTemplateVariables(MethodHandle target, String[] namedVariables, Map templateValues) + public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map templateValues) { if (target == null) { @@ -517,7 +499,7 @@ public abstract class JakartaWebSocketFrameHandlerFactory final int IDX = 1; - MethodHandle retHandle = target; + MethodHolder retHandle = target; if ((templateValues == null) || (templateValues.isEmpty())) { @@ -527,54 +509,54 @@ public abstract class JakartaWebSocketFrameHandlerFactory 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 891c56e22c0..480ec3e89aa 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 class JakartaWebSocketMessageMetadata 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 class JakartaWebSocketMessageMetadata 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 94afefa5b98..194b08a9499 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.CoreSession; 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 @@ public abstract class AbstractDecodedMessageSink implements MessageSink { try { - _methodHandle.invoke(message); + _methodHolder.invoke(message); } catch (Throwable t) { @@ -67,7 +67,7 @@ public abstract class AbstractDecodedMessageSink implements MessageSink } /** - * @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 class AbstractDecodedMessageSink implements MessageSink { 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 class AbstractDecodedMessageSink implements MessageSink { 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 1eab5c76a87..a82280eab4b 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,20 +13,19 @@ 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; 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.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 { - 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 d7d07ad4859..3d9e41af5d8 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 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; 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 e7091d4596e..b540ba57188 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,19 +13,18 @@ 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; 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.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-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 47993416cf1..eee163b2b74 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,34 +15,38 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; 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; 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.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 e9b3cf0b186..dc245da8720 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 jakarta.websocket.Decoder; 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 @@ public abstract class AbstractMessageSinkTest extends AbstractSessionTest 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 abstract class AbstractMessageSinkTest extends AbstractSessionTest 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 aaa05b69ccd..9a6c19bded0 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 @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -31,6 +30,7 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.decoders.RegisteredDecoder 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 +45,7 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest { 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 +67,7 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest { 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 47863ff3271..20ab7969de1 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 @@ -15,7 +15,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; import java.io.IOException; import java.io.InputStream; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -33,6 +32,7 @@ import org.eclipse.jetty.util.BufferUtil; 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 +47,7 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest { 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 +69,7 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest { 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 f4c08ab3172..48a7d980bb5 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 @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; -import java.lang.invoke.MethodHandle; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -31,6 +30,7 @@ import org.eclipse.jetty.ee9.websocket.jakarta.common.decoders.RegisteredDecoder 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 +45,7 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest { 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 +62,7 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest { 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 4dd8cd533ee..b24c5f8d1c0 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 @@ -15,7 +15,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; import java.io.IOException; import java.io.Reader; -import java.lang.invoke.MethodHandle; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -34,6 +33,7 @@ import org.eclipse.jetty.util.FutureCallback; 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 +48,7 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest { 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 +65,7 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest { 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 56ccec6089a..137c74fb15d 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 @@ -16,7 +16,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -33,6 +32,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.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 +46,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 +63,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest 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 +91,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest 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 +113,7 @@ public class InputStreamMessageSinkTest extends AbstractMessageSinkTest 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 795422ecc4f..e51638d6ec2 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 @@ -16,7 +16,6 @@ package org.eclipse.jetty.ee9.websocket.jakarta.common.messages; import java.io.IOException; import java.io.Reader; import java.io.StringWriter; -import java.lang.invoke.MethodHandle; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -28,6 +27,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.messages.ReaderMessageSink; +import org.eclipse.jetty.websocket.core.util.MethodHolder; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -40,7 +40,7 @@ public class ReaderMessageSinkTest extends AbstractMessageSinkTest { 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 +56,7 @@ public class ReaderMessageSinkTest extends AbstractMessageSinkTest { 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 8d8e4373285..fc2525e5d41 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 jakarta.websocket.Session; 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 class InvokerUtilsStaticParamsTest 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 class InvokerUtilsStaticParamsTest // 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 class InvokerUtilsStaticParamsTest }; // 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 class InvokerUtilsStaticParamsTest 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 eec22ab2f5b..2d666bbae69 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.toolchain.test.MavenTestingUtils; 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 class DecoderTextStreamTest extends AbstractClientSessionTest }); 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 6ef8af298cc..c8d5f0d7c96 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.WebSocketException; 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 @@ public class JettyWebSocketFrameHandler implements FrameHandler 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 @@ public class JettyWebSocketFrameHandler implements FrameHandler 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 class JettyWebSocketFrameHandler implements FrameHandler 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 51040f628a6..1b7cb920c85 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.nio.ByteBuffer; 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.PartialStringMessageSink; 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 class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle { 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 class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle 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 class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle { 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, true); } catch (NoSuchMethodException e) diff --git a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java index be891e1cb72..981b10560fa 100644 --- a/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java +++ b/jetty-ee9/jetty-ee9-websocket/jetty-ee9-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee9/websocket/common/OutgoingMessageCapture.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.messages.ByteBufferMessageSink; 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 class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes 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 class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes 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/tests/jetty-jmh/pom.xml b/tests/jetty-jmh/pom.xml index 035f1cff972..5ad89ec81b1 100644 --- a/tests/jetty-jmh/pom.xml +++ b/tests/jetty-jmh/pom.xml @@ -41,6 +41,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 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 00000000000..c531fd4ed48 --- /dev/null +++ b/tests/jetty-jmh/src/main/java/org/eclipse/jetty/websocket/jmh/MethodHolderBenchmark.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// 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.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; +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) +@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +public class MethodHolderBenchmark +{ + private MethodHandle methodHandle; + 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, "consume", methodType); + if (methodHandle == null) + throw new IllegalStateException(); + + 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)); + } + + public void consume(Blackhole blackhole, String a, String b) + { + blackhole.consume(a); + blackhole.consume(b); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void methodHandle() throws Throwable + { + methodHandle.invoke("test", "12"); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void methodHolderNonBinding() throws Throwable + { + methodHolderNonBinding.invoke("test", "12"); + } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + public void methodHolderBinding() throws Throwable + { + methodHolderBinding.invoke("test", "12"); + } + + 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(); + } +} + + From f13a1531940863c71f67361a79ec8bd40f08c859 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 21 Aug 2024 14:19:25 +1000 Subject: [PATCH 2/3] PR #12181 - default to binding MethodHandles Signed-off-by: Lachlan Roberts --- .../org/eclipse/jetty/websocket/core/util/MethodHolder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 e699dcaa7b4..be053d5751e 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 @@ -37,7 +37,8 @@ public interface MethodHolder static MethodHolder from(MethodHandle methodHandle) { String property = System.getProperty(METHOD_HOLDER_BINDING_PROPERTY); - return from(methodHandle, Boolean.parseBoolean(property)); + boolean binding = property == null || Boolean.parseBoolean(property); + return from(methodHandle, binding); } static MethodHolder from(MethodHandle methodHandle, boolean binding) From 3d28e16a844c9ec8578e527391548f01d4a31b9a Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 22 Aug 2024 08:18:49 +1000 Subject: [PATCH 3/3] changes from review Signed-off-by: Lachlan Roberts --- .../core/util/BindingMethodHolder.java | 69 --------- .../websocket/core/util/MethodHolder.java | 142 ++++++++++++++++-- .../core/util/NonBindingMethodHolder.java | 103 ------------- .../websocket/jmh/MethodHolderBenchmark.java | 4 +- 4 files changed, 135 insertions(+), 183 deletions(-) delete mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.java delete mode 100644 jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/NonBindingMethodHolder.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 deleted file mode 100644 index b58f623a215..00000000000 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/BindingMethodHolder.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; - -class BindingMethodHolder implements MethodHolder -{ - public MethodHandle _methodHandle; - - public BindingMethodHolder(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 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/MethodHolder.java b/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/MethodHolder.java index be053d5751e..6d81a90f209 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 @@ -14,6 +14,11 @@ package org.eclipse.jetty.websocket.core.util; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; /** * An interface for managing invocations of methods whose arguments may need to be augmented, by @@ -33,19 +38,18 @@ import java.lang.invoke.MethodHandle; public interface MethodHolder { String METHOD_HOLDER_BINDING_PROPERTY = "jetty.websocket.methodholder.binding"; + boolean IS_BINDING = System.getProperty(METHOD_HOLDER_BINDING_PROPERTY) == null || Boolean.getBoolean(METHOD_HOLDER_BINDING_PROPERTY); static MethodHolder from(MethodHandle methodHandle) { - String property = System.getProperty(METHOD_HOLDER_BINDING_PROPERTY); - boolean binding = property == null || Boolean.parseBoolean(property); - return from(methodHandle, binding); + return from(methodHandle, IS_BINDING); } static MethodHolder from(MethodHandle methodHandle, boolean binding) { if (methodHandle == null) return null; - return binding ? new BindingMethodHolder(methodHandle) : new NonBindingMethodHolder(methodHandle); + return binding ? new Binding(methodHandle) : new NonBinding(methodHandle); } Object invoke(Object... args) throws Throwable; @@ -70,11 +74,6 @@ public interface MethodHolder 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 { switch (args.length) @@ -103,4 +102,129 @@ public interface MethodHolder return methodHandle.invokeWithArguments(args); } } + + class Binding implements MethodHolder + { + public MethodHandle _methodHandle; + + private Binding(MethodHandle methodHandle) + { + _methodHandle = methodHandle; + } + + @Override + public Object invoke(Object... args) throws Throwable + { + return doInvoke(_methodHandle, args); + } + + @Override + public Binding 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(); + } + } + + /** + * 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 NonBinding implements MethodHolder + { + private final MethodHandle _methodHandle; + private final Object[] _parameters; + private final List _unboundParamIndexes = new ArrayList<>(); + + private NonBinding(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 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-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 deleted file mode 100644 index b92fa4c6672..00000000000 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/util/NonBindingMethodHolder.java +++ /dev/null @@ -1,103 +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.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/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 c531fd4ed48..f96ae788e27 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 @@ -98,8 +98,8 @@ public class MethodHolderBenchmark { Options opt = new OptionsBuilder() .include(MethodHolderBenchmark.class.getSimpleName()) - .warmupIterations(5) - .measurementIterations(10) + .warmupIterations(1) + .measurementIterations(5) .forks(1) .threads(1) .build();