diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java index dcd6ad1189b..1ee1797dc86 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java @@ -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); diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java index 659fd033a41..5eeea251808 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java @@ -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) + /** + *
+ * 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. + *
+ *+ * 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. + *
+ * + * @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(); + } + + /** + *+ * 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. + *
+ *+ * 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. + *
+ *+ * 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. + *
+ *+ * {@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. + *
+ * @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 diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java index dae52a2e198..7932300479a 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/messages/DecodedBinaryMessageSink.java @@ -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+ * 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. + *
+ *+ * 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. + *
+ * + * @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(); + } + + /** + *+ * 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. + *
+ *+ * 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. + *
+ *+ * 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. + *
+ *+ * {@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. + *
+ * @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 diff --git a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java index 7976f7d66eb..366827f169f 100644 --- a/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java +++ b/jetty-websocket/websocket-jetty-common/src/test/java/org/eclipse/jetty/websocket/common/OutgoingMessageCapture.java @@ -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));