Merge pull request #4694 from eclipse/jetty-10.0.x-4691-consistent_methodhandles_lookup

Issue #4691 - use static methods with javadoc to get MethodHandle lookups
This commit is contained in:
Lachlan 2020-03-25 10:31:37 +11:00 committed by GitHub
commit ea80253cca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 28 deletions

View File

@ -373,8 +373,7 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
{
try
{
// TODO: move methodhandle lookup to container?
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodHandles.Lookup lookup = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup();
MethodHandle partialMessageHandler = lookup
.findVirtual(MessageHandler.Partial.class, "onMessage", MethodType.methodType(void.class, Object.class, boolean.class));
partialMessageHandler = partialMessageHandler.bindTo(handler);
@ -431,8 +430,7 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
{
try
{
// TODO: move MethodHandle lookup to container?
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodHandles.Lookup lookup = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup();
MethodHandle wholeMsgMethodHandle = lookup.findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class));
wholeMsgMethodHandle = wholeMsgMethodHandle.bindTo(handler);

View File

@ -124,7 +124,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
{
try
{
FILTER_RETURN_TYPE_METHOD = MethodHandles.lookup()
FILTER_RETURN_TYPE_METHOD = getServerMethodHandleLookup()
.findVirtual(JavaxWebSocketSession.class, "filterReturnType", MethodType.methodType(void.class, Object.class));
}
catch (Throwable e)
@ -322,16 +322,17 @@ public abstract class JavaxWebSocketFrameHandlerFactory
try
{
MethodHandles.Lookup lookup = getServerMethodHandleLookup();
if (DecodedMessageSink.class.isAssignableFrom(msgMetadata.sinkClass))
{
MethodHandle ctorHandle = MethodHandles.lookup().findConstructor(msgMetadata.sinkClass,
MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.sinkClass,
MethodType.methodType(void.class, CoreSession.class, msgMetadata.registeredDecoder.interfaceType, MethodHandle.class));
Decoder decoder = session.getDecoders().getInstanceOf(msgMetadata.registeredDecoder);
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), decoder, msgMetadata.handle);
}
else
{
MethodHandle ctorHandle = MethodHandles.lookup().findConstructor(msgMetadata.sinkClass,
MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.sinkClass,
MethodType.methodType(void.class, CoreSession.class, MethodHandle.class));
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.handle);
}
@ -388,7 +389,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
protected JavaxWebSocketFrameHandlerMetadata createEndpointMetadata(Class<? extends javax.websocket.Endpoint> endpointClass, EndpointConfig endpointConfig)
{
JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig);
MethodHandles.Lookup lookup = getMethodHandleLookup(endpointClass);
MethodHandles.Lookup lookup = getApplicationMethodHandleLookup(endpointClass);
Method openMethod = ReflectUtils.findMethod(endpointClass, "onOpen",
javax.websocket.Session.class, javax.websocket.EndpointConfig.class);
@ -410,7 +411,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
protected JavaxWebSocketFrameHandlerMetadata discoverJavaxFrameHandlerMetadata(Class<?> endpointClass, JavaxWebSocketFrameHandlerMetadata metadata)
{
MethodHandles.Lookup lookup = getMethodHandleLookup(endpointClass);
MethodHandles.Lookup lookup = getApplicationMethodHandleLookup(endpointClass);
Method onmethod;
// OnOpen [0..1]
@ -704,9 +705,53 @@ public abstract class JavaxWebSocketFrameHandlerFactory
}
}
private MethodHandles.Lookup getMethodHandleLookup(Class<?> endpointClass)
/**
* <p>
* Gives a {@link MethodHandles.Lookup} instance to be used to find methods in server classes.
* For lookups on application classes use {@link #getApplicationMethodHandleLookup(Class)} instead.
* </p>
* <p>
* This uses the caller sensitive {@link MethodHandles#lookup()}, this will allow MethodHandle access
* to server classes we need to use and will give access permissions to private methods as well.
* </p>
*
* @return a lookup object to be used to find methods on server classes.
*/
public static MethodHandles.Lookup getServerMethodHandleLookup()
{
return MethodHandles.publicLookup().in(endpointClass);
return MethodHandles.lookup();
}
/**
* <p>
* Gives a {@link MethodHandles.Lookup} instance to be used to find public methods in application classes.
* For lookups on server classes use {@link #getServerMethodHandleLookup()} instead.
* </p>
* <p>
* This uses {@link MethodHandles#publicLookup()} as we only need access to public method of the lookupClass.
* To look up a method on the lookupClass, it must be public and the class must be accessible from this
* module, so if the lookupClass is in a JPMS module it must be exported so that the public methods
* of the lookupClass are accessible outside of the module.
* </p>
* <p>
* The {@link java.lang.invoke.MethodHandles.Lookup#in(Class)} allows us to search specifically
* in the endpoint Class to avoid any potential linkage errors which could occur if the same
* class is present in multiple web apps. Unlike using {@link MethodHandles#publicLookup()}
* using {@link MethodHandles#lookup()} with {@link java.lang.invoke.MethodHandles.Lookup#in(Class)}
* will cause the lookup to lose its public access to the lookup class if they are in different modules.
* </p>
* <p>
* {@link MethodHandles#privateLookupIn(Class, MethodHandles.Lookup)} is also unsuitable because it
* requires the caller module to read the target module, and the target module to open reflective
* access to the lookupClasses private methods. This is possible but requires extra configuration
* to provide private access which is not necessary for the purpose of accessing the public methods.
* </p>
* @param lookupClass the desired lookup class for the new lookup object.
* @return a lookup object to be used to find methods on the lookupClass.
*/
public static MethodHandles.Lookup getApplicationMethodHandleLookup(Class<?> lookupClass)
{
return MethodHandles.publicLookup().in(lookupClass);
}
private static class DecodedArgs

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;
import javax.websocket.CloseReason;
@ -28,6 +27,7 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.util.messages.ByteBufferMessageSink;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
@ -44,7 +44,7 @@ public class DecodedBinaryMessageSink<T> extends DecodedMessageSink<Decoder.Bina
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
{
return MethodHandles.lookup().findVirtual(DecodedBinaryMessageSink.class,
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryMessageSink.class,
"onWholeMessage", MethodType.methodType(void.class, ByteBuffer.class))
.bindTo(this);
}

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
@ -28,6 +27,7 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.util.messages.InputStreamMessageSink;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
@ -44,7 +44,7 @@ public class DecodedBinaryStreamMessageSink<T> extends DecodedMessageSink<Decode
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
{
return MethodHandles.lookup().findVirtual(DecodedBinaryStreamMessageSink.class,
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryStreamMessageSink.class,
"onStreamStart", MethodType.methodType(void.class, InputStream.class))
.bindTo(this);
}

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
@ -27,6 +26,7 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
import org.eclipse.jetty.websocket.util.messages.StringMessageSink;
@ -43,7 +43,7 @@ public class DecodedTextMessageSink<T> extends DecodedMessageSink<Decoder.Text<T
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
{
return MethodHandles.lookup().findVirtual(DecodedTextMessageSink.class,
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextMessageSink.class,
"onWholeMessage", MethodType.methodType(void.class, String.class))
.bindTo(this);
}

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.io.Reader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
@ -28,6 +27,7 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
import org.eclipse.jetty.websocket.util.messages.ReaderMessageSink;
@ -44,7 +44,7 @@ public class DecodedTextStreamMessageSink<T> extends DecodedMessageSink<Decoder.
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
{
return MethodHandles.lookup().findVirtual(DecodedTextStreamMessageSink.class,
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextStreamMessageSink.class,
"onStreamStart", MethodType.methodType(void.class, Reader.class))
.bindTo(this);
}

View File

@ -19,11 +19,11 @@
package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
import org.eclipse.jetty.websocket.javax.common.AbstractSessionTest;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
public abstract class AbstractMessageSinkTest extends AbstractSessionTest
{
@ -34,7 +34,7 @@ public abstract class AbstractMessageSinkTest extends AbstractSessionTest
Class<?> refc = copy.getClass();
String name = "accept";
MethodType methodType = MethodType.methodType(void.class, type);
MethodHandle handle = MethodHandles.lookup().findVirtual(refc, name, methodType);
MethodHandle handle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(refc, name, methodType);
return handle.bindTo(copy);
}
catch (NoSuchMethodException | IllegalAccessException e)

View File

@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.util.InvokerUtils;
import org.eclipse.jetty.websocket.util.ReflectUtils;
@ -31,7 +32,8 @@ public class CompletableFutureMethodHandle
public static <T> MethodHandle of(Class<T> type, CompletableFuture<T> future)
{
Method method = ReflectUtils.findMethod(CompletableFuture.class, "complete", type);
MethodHandle completeHandle = InvokerUtils.mutatedInvoker(MethodHandles.lookup(), CompletableFuture.class, method, new InvokerUtils.Arg(type));
MethodHandles.Lookup lookup = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup();
MethodHandle completeHandle = InvokerUtils.mutatedInvoker(lookup, CompletableFuture.class, method, new InvokerUtils.Arg(type));
return completeHandle.bindTo(future);
}
}

View File

@ -157,7 +157,8 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
try
{
MethodHandle ctorHandle = MethodHandles.lookup().findConstructor(sinkClass,
MethodHandles.Lookup lookup = JettyWebSocketFrameHandlerFactory.getServerMethodHandleLookup();
MethodHandle ctorHandle = lookup.findConstructor(sinkClass,
MethodType.methodType(void.class, CoreSession.class, MethodHandle.class));
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgHandle);
}
@ -194,7 +195,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
private JettyWebSocketFrameHandlerMetadata createListenerMetadata(Class<?> endpointClass)
{
JettyWebSocketFrameHandlerMetadata metadata = new JettyWebSocketFrameHandlerMetadata();
MethodHandles.Lookup lookup = getMethodHandleLookup(endpointClass);
MethodHandles.Lookup lookup = JettyWebSocketFrameHandlerFactory.getApplicationMethodHandleLookup(endpointClass);
Method openMethod = ReflectUtils.findMethod(endpointClass, "onWebSocketConnect", Session.class);
MethodHandle open = toMethodHandle(lookup, openMethod);
@ -273,7 +274,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
metadata.setIdleTimeout(Duration.ofMillis(max));
metadata.setBatchMode(anno.batchMode());
MethodHandles.Lookup lookup = getMethodHandleLookup(endpointClass);
MethodHandles.Lookup lookup = getApplicationMethodHandleLookup(endpointClass);
Method onmethod;
// OnWebSocketConnect [0..1]
@ -456,9 +457,53 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
throw new InvalidSignatureException(err.toString());
}
private MethodHandles.Lookup getMethodHandleLookup(Class<?> endpointClass)
/**
* <p>
* Gives a {@link MethodHandles.Lookup} instance to be used to find methods in server classes.
* For lookups on application classes use {@link #getApplicationMethodHandleLookup(Class)} instead.
* </p>
* <p>
* This uses the caller sensitive {@link MethodHandles#lookup()}, this will allow MethodHandle access
* to server classes we need to use and will give access permissions to private methods as well.
* </p>
*
* @return a lookup object to be used to find methods on server classes.
*/
public static MethodHandles.Lookup getServerMethodHandleLookup()
{
return MethodHandles.publicLookup().in(endpointClass);
return MethodHandles.lookup();
}
/**
* <p>
* Gives a {@link MethodHandles.Lookup} instance to be used to find public methods in application classes.
* For lookups on server classes use {@link #getServerMethodHandleLookup()} instead.
* </p>
* <p>
* This uses {@link MethodHandles#publicLookup()} as we only need access to public method of the lookupClass.
* To look up a method on the lookupClass, it must be public and the class must be accessible from this
* module, so if the lookupClass is in a JPMS module it must be exported so that the public methods
* of the lookupClass are accessible outside of the module.
* </p>
* <p>
* The {@link java.lang.invoke.MethodHandles.Lookup#in(Class)} allows us to search specifically
* in the endpoint Class to avoid any potential linkage errors which could occur if the same
* class is present in multiple web apps. Unlike using {@link MethodHandles#publicLookup()}
* using {@link MethodHandles#lookup()} with {@link java.lang.invoke.MethodHandles.Lookup#in(Class)}
* will cause the lookup to lose its public access to the lookup class if they are in different modules.
* </p>
* <p>
* {@link MethodHandles#privateLookupIn(Class, MethodHandles.Lookup)} is also unsuitable because it
* requires the caller module to read the target module, and the target module to open reflective
* access to the lookupClasses private methods. This is possible but requires extra configuration
* to provide private access which is not necessary for the purpose of accessing the public methods.
* </p>
* @param lookupClass the desired lookup class for the new lookup object.
* @return a lookup object to be used to find methods on the lookupClass.
*/
public static MethodHandles.Lookup getApplicationMethodHandleLookup(Class<?> lookupClass)
{
return MethodHandles.publicLookup().in(lookupClass);
}
@Override

View File

@ -52,7 +52,7 @@ public class OutgoingMessageCapture extends CoreSession.Empty implements CoreSes
public OutgoingMessageCapture()
{
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandles.Lookup lookup = JettyWebSocketFrameHandlerFactory.getApplicationMethodHandleLookup(this.getClass());
try
{
MethodHandle text = lookup.findVirtual(this.getClass(), "onWholeText", MethodType.methodType(Void.TYPE, String.class));