Revert Spigot PluginClassLoader workaround

This commit is contained in:
Luck 2017-12-29 20:40:43 +00:00
parent 9bb1ba12b2
commit 717347273d
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
6 changed files with 19 additions and 278 deletions

View File

@ -31,7 +31,6 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LuckPermsApi; import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.platform.PlatformType; import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.bukkit.calculators.BukkitCalculatorFactory; import me.lucko.luckperms.bukkit.calculators.BukkitCalculatorFactory;
import me.lucko.luckperms.bukkit.classloader.LPClassLoader;
import me.lucko.luckperms.bukkit.contexts.BukkitContextManager; import me.lucko.luckperms.bukkit.contexts.BukkitContextManager;
import me.lucko.luckperms.bukkit.contexts.WorldCalculator; import me.lucko.luckperms.bukkit.contexts.WorldCalculator;
import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener; import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener;
@ -96,7 +95,6 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -131,7 +129,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private DefaultsProvider defaultsProvider; private DefaultsProvider defaultsProvider;
private ChildPermissionProvider childPermissionProvider; private ChildPermissionProvider childPermissionProvider;
private LocaleManager localeManager; private LocaleManager localeManager;
private LPClassLoader lpClassLoader;
private DependencyManager dependencyManager; private DependencyManager dependencyManager;
private CachedStateManager cachedStateManager; private CachedStateManager cachedStateManager;
private ContextManager<Player> contextManager; private ContextManager<Player> contextManager;
@ -156,7 +153,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
senderFactory = new BukkitSenderFactory(this); senderFactory = new BukkitSenderFactory(this);
log = new SenderLogger(this, getConsoleSender()); log = new SenderLogger(this, getConsoleSender());
lpClassLoader = LPClassLoader.obtainFor(this);
dependencyManager = new DependencyManager(this); dependencyManager = new DependencyManager(this);
dependencyManager.loadDependencies(Collections.singleton(Dependency.CAFFEINE)); dependencyManager.loadDependencies(Collections.singleton(Dependency.CAFFEINE));
} }
@ -393,11 +389,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
getLog().info("Goodbye!"); getLog().info("Goodbye!");
} }
@Override
public void loadUrlIntoClasspath(URL url) {
lpClassLoader.addURL(url);
}
public void tryVaultHook(boolean force) { public void tryVaultHook(boolean force) {
if (vaultHookManager != null) { if (vaultHookManager != null) {
return; // already hooked return; // already hooked

View File

@ -1,47 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.classloader;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.dependencies.DependencyManager;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.net.URL;
/**
* A dummy implementation of {@link LPClassLoader} which just falls back to the
* reflection method used by other platforms.
*/
@RequiredArgsConstructor
public class FallbackClassLoader implements LPClassLoader {
private final LuckPermsPlugin plugin;
@Override
public void addURL(URL url) {
DependencyManager.loadUrlIntoClassLoader(url, plugin.getClass().getClassLoader());
}
}

View File

@ -1,146 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.classloader;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;
import org.bukkit.plugin.java.JavaPlugin;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A fake classloader instance which sits in-front of a PluginClassLoader, and
* attempts to load classes from it's own sources before allowing the PCL to load
* the class.
*
* This allows us to inject extra URL sources into the plugin's classloader at
* runtime.
*/
public class InjectedClassLoader extends URLClassLoader implements LPClassLoader {
public static InjectedClassLoader inject(JavaPlugin plugin) throws Exception {
// get the plugin's PluginClassLoader instance
ClassLoader classLoader = plugin.getClass().getClassLoader();
// get a ref to the PCL class
Class<?> pclClass = Class.forName("org.bukkit.plugin.java.PluginClassLoader");
// extract the 'classes' cache map
Field classesField = pclClass.getDeclaredField("classes");
classesField.setAccessible(true);
// obtain the classes instance from the classloader
//noinspection unchecked
Map<String, Class<?>> old = (Map) classesField.get(classLoader);
// init a new InjectedClassLoader to read from
InjectedClassLoader newLoader = new InjectedClassLoader();
// replace the 'classes' cache map with our own.
classesField.set(classLoader, new DelegatingClassMap(newLoader, old));
return newLoader;
}
static {
try {
Method method = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable");
if (method != null) {
method.setAccessible(true);
method.invoke(null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private final Map<String, Class<?>> cache = new ConcurrentHashMap<>();
private InjectedClassLoader() {
super(new URL[0]);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
private Class<?> lookup(String name) {
// try the cache
Class<?> clazz = cache.get(name);
if (clazz != null) {
return clazz;
}
try {
// attempt to load
clazz = loadClass(name);
// if successful, add to the cache file
cache.put(name, clazz);
return clazz;
} catch (ClassNotFoundException e) {
return null;
}
}
/**
* A fake map instance which effectively allows us to override the behaviour
* of #findClass in PluginClassLoader.
*/
@RequiredArgsConstructor
private static final class DelegatingClassMap implements Map<String, Class<?>> {
private final InjectedClassLoader loader;
// delegate all other calls to the original map
@Delegate(excludes = Exclude.class)
private final Map<String, Class<?>> delegate;
// override the #get call, so we can attempt to load the class ourselves.
@Override
public Class<?> get(Object key) {
String className = ((String) key);
Class<?> clazz = loader.lookup(className);
if (clazz != null) {
return clazz;
} else {
return delegate.get(className);
}
}
private interface Exclude {
Class<?> get(Object key);
}
}
}

View File

@ -1,54 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.classloader;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import java.net.URL;
/**
* An interface over a {@link org.bukkit.plugin.java.JavaPlugin}'s PluginClassLoader.
*/
public interface LPClassLoader {
static LPClassLoader obtainFor(LPBukkitPlugin plugin) {
try {
// try to inject into the classloader
return InjectedClassLoader.inject(plugin);
} catch (Exception e) {
// fallback to a reflection based method
return new FallbackClassLoader(plugin);
}
}
/**
* Adds a URL to the classloader
*
* @param url the url to add
*/
void addURL(URL url);
}

View File

@ -103,10 +103,14 @@ public class DependencyManager {
dependencies.add(Dependency.JEDIS); dependencies.add(Dependency.JEDIS);
} }
// don't load slf4j or configurate dependencies on sponge, as they're already present // don't load slf4j if it's already present
if (plugin.getServerType() == PlatformType.SPONGE) { if (classExists("org.slf4j.Logger") && classExists("org.slf4j.LoggerFactory")) {
dependencies.remove(Dependency.SLF4J_API); dependencies.remove(Dependency.SLF4J_API);
dependencies.remove(Dependency.SLF4J_SIMPLE); dependencies.remove(Dependency.SLF4J_SIMPLE);
}
// don't load configurate dependencies on sponge
if (plugin.getServerType() == PlatformType.SPONGE) {
dependencies.remove(Dependency.CONFIGURATE_CORE); dependencies.remove(Dependency.CONFIGURATE_CORE);
dependencies.remove(Dependency.CONFIGURATE_GSON); dependencies.remove(Dependency.CONFIGURATE_GSON);
dependencies.remove(Dependency.CONFIGURATE_YAML); dependencies.remove(Dependency.CONFIGURATE_YAML);
@ -182,20 +186,14 @@ public class DependencyManager {
} }
} }
private void loadJar(File file) { private void loadJar(File file) {
// get the classloader to load into // get the classloader to load into
try { ClassLoader classLoader = plugin.getClass().getClassLoader();
plugin.loadUrlIntoClasspath(file.toURI().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
public static void loadUrlIntoClassLoader(URL url, ClassLoader classLoader) {
if (classLoader instanceof URLClassLoader) { if (classLoader instanceof URLClassLoader) {
try { try {
ADD_URL_METHOD.invoke(classLoader, url); ADD_URL_METHOD.invoke(classLoader, file.toURI().toURL());
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException | MalformedURLException e) {
throw new RuntimeException("Unable to invoke URLClassLoader#addURL", e); throw new RuntimeException("Unable to invoke URLClassLoader#addURL", e);
} }
} else { } else {
@ -203,4 +201,13 @@ public class DependencyManager {
} }
} }
private static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
} }

View File

@ -55,7 +55,6 @@ import me.lucko.luckperms.common.verbose.VerboseHandler;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -155,15 +154,6 @@ public interface LuckPermsPlugin {
*/ */
DependencyManager getDependencyManager(); DependencyManager getDependencyManager();
/**
* Loads a dependency into the plugins classpath
*
* @param url the url to load
*/
default void loadUrlIntoClasspath(URL url) {
DependencyManager.loadUrlIntoClassLoader(url, getClass().getClassLoader());
}
/** /**
* Gets the context manager. * Gets the context manager.
* This object handles context accumulation for all players on the platform. * This object handles context accumulation for all players on the platform.