Properly open URLClassLoader module on Java 9+ to allow reflection and avoid access warning (#952)

This commit is contained in:
Luck 2020-11-26 15:52:39 +00:00
parent 53fb46ee85
commit 8a38c5f4b7
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B

View File

@ -25,9 +25,6 @@
package me.lucko.luckperms.common.dependencies.classloader;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
import java.lang.reflect.InvocationTargetException;
@ -38,10 +35,28 @@ import java.net.URLClassLoader;
import java.nio.file.Path;
public class ReflectionClassLoader implements PluginClassLoader {
private final URLClassLoader classLoader;
@SuppressWarnings("Guava") // we can't use java.util.Function because old Guava versions are used at runtime
private final Supplier<Method> addUrlMethod;
private static final Method ADD_URL_METHOD;
static {
// If on Java 9+, open the URLClassLoader module to this module
// so we can access its API via reflection without producing a warning.
try {
openUrlClassLoaderModule();
} catch (Throwable e) {
// ignore exception - will throw on Java 8 since the Module classes don't exist
}
// Get the protected 'addURL' method on URLClassLoader and set it to accessible.
try {
ADD_URL_METHOD = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
ADD_URL_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private final URLClassLoader classLoader;
public ReflectionClassLoader(LuckPermsBootstrap bootstrap) throws IllegalStateException {
ClassLoader classLoader = bootstrap.getClass().getClassLoader();
@ -50,42 +65,35 @@ public class ReflectionClassLoader implements PluginClassLoader {
} else {
throw new IllegalStateException("ClassLoader is not instance of URLClassLoader");
}
this.addUrlMethod = Suppliers.memoize(() -> {
if (isJava9OrNewer()) {
bootstrap.getPluginLogger().info("It is safe to ignore any warning printed following this message " +
"starting with 'WARNING: An illegal reflective access operation has occurred, Illegal reflective " +
"access by " + getClass().getName() + "'. This is intended, and will not have any impact on the " +
"operation of LuckPerms.");
}
try {
Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
return addUrlMethod;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
@Override
public void addJarToClasspath(Path file) {
try {
this.addUrlMethod.get().invoke(this.classLoader, file.toUri().toURL());
ADD_URL_METHOD.invoke(this.classLoader, file.toUri().toURL());
} catch (IllegalAccessException | InvocationTargetException | MalformedURLException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("JavaReflectionMemberAccess")
private static boolean isJava9OrNewer() {
try {
// method was added in the Java 9 release
Runtime.class.getMethod("version");
return true;
} catch (NoSuchMethodException e) {
return false;
}
private static void openUrlClassLoaderModule() throws Exception {
// This is effectively calling:
//
// URLClassLoader.class.getModule().addOpens(
// URLClassLoader.class.getPackageName(),
// ReflectionClassLoader.class.getModule()
// );
//
// We use reflection since we build against Java 8.
Class<?> moduleClass = Class.forName("java.lang.Module");
Method getModuleMethod = Class.class.getMethod("getModule");
Method addOpensMethod = moduleClass.getMethod("addOpens", String.class, moduleClass);
Object urlClassLoaderModule = getModuleMethod.invoke(URLClassLoader.class);
Object thisModule = getModuleMethod.invoke(ReflectionClassLoader.class);
addOpensMethod.invoke(urlClassLoaderModule, URLClassLoader.class.getPackage().getName(), thisModule);
}
}