Issue #4800 - prevent LinkageErrors for WS lookup on JDK 14

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2020-04-24 13:31:29 +10:00
parent f5b0dc56d2
commit 870a765464
3 changed files with 35 additions and 12 deletions

View File

@ -727,10 +727,10 @@ public abstract class JavaxWebSocketFrameHandlerFactory
* 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.
* This method needs to return a Lookup instance which has only {@link java.lang.invoke.MethodHandles.Lookup#PUBLIC}
* access on the lookup class only. Previously we could use {@link MethodHandles#publicLookup()}, however
* in JDK 14 this was changed to only include {@link java.lang.invoke.MethodHandles.Lookup#UNCONDITIONAL},
* so instead we use {@link MethodHandles#lookup()} and manually drop all access modes we don't want to allow.
* </p>
* <p>
* The {@link java.lang.invoke.MethodHandles.Lookup#in(Class)} allows us to search specifically
@ -740,6 +740,11 @@ public abstract class JavaxWebSocketFrameHandlerFactory
* will cause the lookup to lose its public access to the lookup class if they are in different modules.
* </p>
* <p>
* 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>
* {@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
@ -750,7 +755,14 @@ public abstract class JavaxWebSocketFrameHandlerFactory
*/
public static MethodHandles.Lookup getApplicationMethodHandleLookup(Class<?> lookupClass)
{
return MethodHandles.publicLookup().in(lookupClass);
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup = lookup
.dropLookupMode(MethodHandles.Lookup.UNCONDITIONAL)
.dropLookupMode(MethodHandles.Lookup.MODULE)
.dropLookupMode(MethodHandles.Lookup.PRIVATE)
.dropLookupMode(MethodHandles.Lookup.PACKAGE)
.dropLookupMode(MethodHandles.Lookup.PROTECTED);
return lookup.in(lookupClass);
}
private static class DecodedArgs

View File

@ -473,10 +473,10 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
* 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.
* This method needs to return a Lookup instance which has only {@link java.lang.invoke.MethodHandles.Lookup#PUBLIC}
* access on the lookup class only. Previously we could use {@link MethodHandles#publicLookup()}, however
* in JDK 14 this was changed to only include {@link java.lang.invoke.MethodHandles.Lookup#UNCONDITIONAL},
* so instead we use {@link MethodHandles#lookup()} and manually drop all access modes we don't want to allow.
* </p>
* <p>
* The {@link java.lang.invoke.MethodHandles.Lookup#in(Class)} allows us to search specifically
@ -486,6 +486,11 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
* will cause the lookup to lose its public access to the lookup class if they are in different modules.
* </p>
* <p>
* 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>
* {@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
@ -496,7 +501,14 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
*/
public static MethodHandles.Lookup getApplicationMethodHandleLookup(Class<?> lookupClass)
{
return MethodHandles.publicLookup().in(lookupClass);
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup = lookup
.dropLookupMode(MethodHandles.Lookup.UNCONDITIONAL)
.dropLookupMode(MethodHandles.Lookup.MODULE)
.dropLookupMode(MethodHandles.Lookup.PRIVATE)
.dropLookupMode(MethodHandles.Lookup.PACKAGE)
.dropLookupMode(MethodHandles.Lookup.PROTECTED);
return lookup.in(lookupClass);
}
@Override

View File

@ -40,7 +40,6 @@ import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.JRE;
@ -441,7 +440,7 @@ public class DistributionTests extends AbstractDistributionTest
{
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
// we do not test that anymore because it doesn't work for java14
//assertFalse(run2.getLogs().stream().anyMatch(s -> s.contains("LinkageError")));
assertFalse(run2.getLogs().stream().anyMatch(s -> s.contains("LinkageError")));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test1/index.jsp");