Issue #6328 - avoid binding WebSocket MethodHandles

Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
This commit is contained in:
Lachlan Roberts 2024-08-21 10:51:16 +10:00
parent 2ba2250ac8
commit e11120c3d1
No known key found for this signature in database
GPG Key ID: 5663FB7A8FF7E348
81 changed files with 1591 additions and 909 deletions

View File

@ -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;
/**
* <p>Abstract implementation of {@link MessageSink}.</p>
@ -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;
}

View File

@ -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;
/**
* <p>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

View File

@ -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;
/**
* <p>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();
}
}

View File

@ -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;
/**
* <p>A partial implementation of {@link MessageSink} for methods that consume WebSocket
@ -51,9 +51,9 @@ public abstract class DispatchedMessageSink extends AbstractMessageSink
private volatile CompletableFuture<Void> 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);

View File

@ -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

View File

@ -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;
/**
* <p>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();
}

View File

@ -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;
/**
* <p>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();
}
}

View File

@ -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;
/**
* <p>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();

View File

@ -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

View File

@ -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;
/**
* <p>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();

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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:
* <ul>
* <li>direct method invocation on an held object</li>
* <li>calling a method pointer</li>
* <li>calling a MethodHandle bound to the known arguments</li>
* <li>calling a MethodHandle without binding to the known arguments</li>
* </ul>
*
* 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);
}
}
}

View File

@ -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<Integer> _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();
}
}

View File

@ -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

View File

@ -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});

View File

@ -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<? extends MessageSink> textSinkClass;
private final Class<? extends MessageSink> 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<? extends MessageSink> sinkClass, WebSocketSession session, MethodHandle msgHandle, boolean autoDemanding)
private static MessageSink createMessageSink(Class<? extends MessageSink> 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)

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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:

View File

@ -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<T> implements MethodHolder
{
private final MessageHandler.Partial<T> _messageHandler;
public JakartaMessagePartialMethodHolder(MessageHandler.Partial<T> 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;
}
}

View File

@ -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<T> implements MethodHolder
{
private final MessageHandler.Whole<T> _messageHandler;
public JakartaMessageWholeMethodHolder(MessageHandler.Whole<T> 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;
}
}

View File

@ -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 <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> 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 <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> 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)

View File

@ -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<RegisteredDecoder> 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<String, String> templateValues)
public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map<String, String> 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
{

View File

@ -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<? extends MessageSink> sinkClass;
private List<RegisteredDecoder> 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<? extends MessageSink> getSinkClass()

View File

@ -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<T> _decoders;
public Basic(CoreSession coreSession, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public Basic(CoreSession coreSession, MethodHolder methodHolder, List<RegisteredDecoder> 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<RegisteredDecoder> decoders)
public Stream(CoreSession coreSession, MethodHolder methodHolder, List<RegisteredDecoder> 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();

View File

@ -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<T> extends AbstractDecodedMessageSink.Basi
{
private static final Logger LOG = LoggerFactory.getLogger(DecodedBinaryMessageSink.class);
public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedBinaryMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Stream<Decoder.BinaryStream<T>>
{
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Basic<
{
private static final Logger LOG = LoggerFactory.getLogger(DecodedTextMessageSink.class);
public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedTextMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Stream<Decoder.TextStream<T>>
{
public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedTextStreamMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedBinaryMessageSink<Calendar> 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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedBinaryStreamMessageSink<Calendar> 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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedTextMessageSink<Calendar> 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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedTextStreamMessageSink<Calendar> 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<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(copyHandle), decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -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();

View File

@ -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<StringWriter> 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<StringWriter> 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();

View File

@ -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<String, String> 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<String, String> 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<String, String> 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)"));
}
}

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(quoteHandle), decoders);
List<FutureCallback> callbacks = new ArrayList<>();
FutureCallback finCallback = null;

View File

@ -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<T> implements MethodHolder
{
private final MessageHandler.Partial<T> _messageHandler;
public JakartaMessagePartialMethodHolder(MessageHandler.Partial<T> 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;
}
}

View File

@ -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<T> implements MethodHolder
{
private final MessageHandler.Whole<T> _messageHandler;
public JakartaMessageWholeMethodHolder(MessageHandler.Whole<T> 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;
}
}

View File

@ -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 <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> 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 <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> 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)

View File

@ -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<RegisteredDecoder> 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<String, String> templateValues)
public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map<String, String> 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
{

View File

@ -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<? extends MessageSink> sinkClass;
private List<RegisteredDecoder> 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<? extends MessageSink> getSinkClass()

View File

@ -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<T> _decoders;
public Basic(CoreSession coreSession, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public Basic(CoreSession coreSession, MethodHolder methodHolder, List<RegisteredDecoder> 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<RegisteredDecoder> decoders)
public Stream(CoreSession coreSession, MethodHolder methodHolder, List<RegisteredDecoder> 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();

View File

@ -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<T> extends AbstractDecodedMessageSink.Basi
{
private static final Logger LOG = LoggerFactory.getLogger(DecodedBinaryMessageSink.class);
public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedBinaryMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Stream<Decoder.BinaryStream<T>>
{
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Basic<
{
private static final Logger LOG = LoggerFactory.getLogger(DecodedTextMessageSink.class);
public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedTextMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Stream<Decoder.TextStream<T>>
{
public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedTextStreamMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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 <T> MethodHandle getAcceptHandle(Consumer<T> copy, Class<T> type)
public <T> MethodHolder getAcceptHandle(Consumer<T> copy, Class<T> 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)
{

View File

@ -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<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -67,7 +67,7 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -70,7 +70,7 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -63,7 +63,7 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -66,7 +66,7 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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();

View File

@ -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<StringWriter> 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<StringWriter> 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();

View File

@ -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<String, String> 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<String, String> 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<String, String> 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)"));
}
}

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(quoteHandle), decoders);
List<FutureCallback> callbacks = new ArrayList<>();
FutureCallback finCallback = null;

View File

@ -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<T> implements MethodHolder
{
private final MessageHandler.Partial<T> _messageHandler;
public JakartaMessagePartialMethodHolder(MessageHandler.Partial<T> 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;
}
}

View File

@ -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<T> implements MethodHolder
{
private final MessageHandler.Whole<T> _messageHandler;
public JakartaMessageWholeMethodHolder(MessageHandler.Whole<T> 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;
}
}

View File

@ -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 <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> 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 <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> 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)

View File

@ -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<RegisteredDecoder> 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<String, String> templateValues)
public static MethodHolder bindTemplateVariables(MethodHolder target, String[] namedVariables, Map<String, String> 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
{

View File

@ -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<? extends MessageSink> sinkClass;
private List<RegisteredDecoder> 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<? extends MessageSink> getSinkClass()

View File

@ -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<T> _decoders;
public Basic(CoreSession coreSession, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public Basic(CoreSession coreSession, MethodHolder methodHolder, List<RegisteredDecoder> 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<RegisteredDecoder> decoders)
public Stream(CoreSession coreSession, MethodHolder methodHolder, List<RegisteredDecoder> 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();

View File

@ -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<T> extends AbstractDecodedMessageSink.Basi
{
private static final Logger LOG = LoggerFactory.getLogger(DecodedBinaryMessageSink.class);
public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedBinaryMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Stream<Decoder.BinaryStream<T>>
{
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Basic<
{
private static final Logger LOG = LoggerFactory.getLogger(DecodedTextMessageSink.class);
public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedTextMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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<T> extends AbstractDecodedMessageSink.Stream<Decoder.TextStream<T>>
{
public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
public DecodedTextStreamMessageSink(CoreSession session, MethodHolder methodHolder, List<RegisteredDecoder> 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)

View File

@ -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 <T> MethodHandle getAcceptHandle(Consumer<T> copy, Class<T> type)
public <T> MethodHolder getAcceptHandle(Consumer<T> copy, Class<T> 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)
{

View File

@ -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<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders);
@ -67,7 +67,7 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders);

View File

@ -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<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -69,7 +69,7 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
MethodHolder copyHandle = getAcceptHandle(copy, Calendar.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -62,7 +62,7 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
@ -65,7 +65,7 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest
{
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
MethodHolder copyHandle = getAcceptHandle(copy, Date.class);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);

View File

@ -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();

View File

@ -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<StringWriter> 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<StringWriter> 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();

View File

@ -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<String, String> 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<String, String> 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<String, String> 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)"));
}
}

View File

@ -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<RegisteredDecoder> decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), MethodHolder.from(quoteHandle), decoders);
List<FutureCallback> callbacks = new ArrayList<>();
FutureCallback finCallback = null;

View File

@ -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<? extends MessageSink> textSinkClass;
private MethodHandle binaryHandle;
private MethodHolder binaryHandle;
private final Class<? extends MessageSink> 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<? extends MessageSink> textSinkClass,
Class<? extends MessageSink> 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));

View File

@ -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<? extends MessageSink> textSinkClass = metadata.getTextSink();
final Class<? extends MessageSink> 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<? extends MessageSink> sinkClass, WebSocketSession session)
public static MessageSink createMessageSink(MethodHolder msgHandle, Class<? extends MessageSink> 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)

View File

@ -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:

View File

@ -41,6 +41,14 @@
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>

View File

@ -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();
}
}