From 71b6a5fa02eb779856a48c0018efd647cdb89f16 Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Mon, 20 Jul 2020 00:37:16 +0200 Subject: [PATCH] v2.0.5 - Fix for reloading ourselves --- .../bukkit/commands/CommandServerUtils.java | 11 +++++ .../bukkit/managers/BukkitPluginManager.java | 27 ++++++++++-- .../bukkit/reflection/RJavaPlugin.java | 26 +++++++++++ .../bukkit/reflection/RJavaPluginLoader.java | 43 +++++++++++++++++++ .../bukkit/reflection/RPluginClassLoader.java | 22 +++++----- .../bungee/commands/CommandServerUtils.java | 11 +++++ .../common/entities/CloseableResult.java | 26 ++++++++--- build.gradle | 2 +- 8 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/CommandServerUtils.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/CommandServerUtils.java index 1a5f1a8..b894120 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/CommandServerUtils.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/CommandServerUtils.java @@ -32,6 +32,7 @@ import net.frankheijden.serverutils.common.utils.ForwardFilter; import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListFormat; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginIdentifiableCommand; @@ -195,6 +196,16 @@ public class CommandServerUtils extends BaseCommand { @CommandPermission("serverutils.reloadplugin") @Description("Reloads a specified plugin.") public void onReloadPlugin(CommandSender sender, String pluginName) { + if (pluginName.equalsIgnoreCase("ServerUtils")) { + String result = BukkitPluginManager.get().reloadPlugin(pluginName).toString(); + if (result.equals("SUCCESS")) { + sender.sendMessage(ChatColor.GREEN + "Successfully reloaded ServerUtils."); + } else { + sender.sendMessage(ChatColor.RED + "Something went wrong reloading ServerUtils."); + } + return; + } + Result result = BukkitPluginManager.get().reloadPlugin(pluginName); result.sendTo(BukkitUtils.wrap(sender), "reload", pluginName); } diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java index b4f7174..d2b9314 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java @@ -2,6 +2,8 @@ package net.frankheijden.serverutils.bukkit.managers; import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -16,6 +18,7 @@ import net.frankheijden.serverutils.bukkit.reflection.RCommandMap; import net.frankheijden.serverutils.bukkit.reflection.RCraftServer; import net.frankheijden.serverutils.bukkit.reflection.RCraftingManager; import net.frankheijden.serverutils.bukkit.reflection.RJavaPlugin; +import net.frankheijden.serverutils.bukkit.reflection.RJavaPluginLoader; import net.frankheijden.serverutils.bukkit.reflection.RPluginClassLoader; import net.frankheijden.serverutils.bukkit.reflection.RSimplePluginManager; import net.frankheijden.serverutils.common.entities.CloseableResult; @@ -136,16 +139,34 @@ public class BukkitPluginManager extends AbstractPluginManager { @Override public CloseableResult unloadPlugin(Plugin plugin) { if (plugin == null) return new CloseableResult(Result.NOT_EXISTS); - Closeable closeable; + List closeables = new ArrayList<>(); try { RSimplePluginManager.getPlugins(Bukkit.getPluginManager()).remove(plugin); RSimplePluginManager.removeLookupName(Bukkit.getPluginManager(), plugin.getName()); - closeable = RPluginClassLoader.clearClassLoader(RJavaPlugin.getClassLoader(plugin)); + + ClassLoader loader = RJavaPlugin.getClassLoader(plugin); + RPluginClassLoader.clearClassLoader(loader); + addIfInstance(closeables, (Closeable) () -> { + try { + Map> classes = RPluginClassLoader.getClasses(loader); + RJavaPluginLoader.removeClasses(getPluginLoader(getPluginFile(plugin)), classes.keySet()); + } catch (IllegalAccessException ex) { + throw new IOException(ex); + } + }); + addIfInstance(closeables, loader); + addIfInstance(closeables, RJavaPlugin.clearJavaPlugin(plugin)); } catch (Exception ex) { ex.printStackTrace(); return new CloseableResult(Result.ERROR); } - return new CloseableResult(closeable); + return new CloseableResult(closeables); + } + + private static void addIfInstance(List list, Object obj) { + if (obj instanceof Closeable) { + list.add((Closeable) obj); + } } /** diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java index 185ac4e..20a5eee 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java @@ -1,10 +1,15 @@ package net.frankheijden.serverutils.bukkit.reflection; +import static net.frankheijden.serverutils.common.reflection.FieldParam.fieldOf; import static net.frankheijden.serverutils.common.reflection.MethodParam.methodOf; +import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.getAllFields; import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.getAllMethods; import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.invoke; +import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.set; +import java.io.Closeable; import java.io.File; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; @@ -14,13 +19,18 @@ import org.bukkit.plugin.java.JavaPlugin; public class RJavaPlugin { private static Class javaPluginClass; + private static Map fields; private static Map methods; static { try { javaPluginClass = JavaPlugin.class; + fields = getAllFields(javaPluginClass, + fieldOf("loader"), + fieldOf("classLoader")); methods = getAllMethods(javaPluginClass, methodOf("getClassLoader"), + methodOf("getClass"), methodOf("getFile")); } catch (Exception ex) { ex.printStackTrace(); @@ -34,4 +44,20 @@ public class RJavaPlugin { public static File getFile(Object instance) throws ReflectiveOperationException { return (File) invoke(methods, instance, "getFile"); } + + /** + * Clears the JavaPlugin from instances and returns the classloader associated with it. + * @param instance The instance of the JavaPlugin. + * @return The classloader associated with it. + * @throws ReflectiveOperationException When a reflection error occurred. + */ + public static Closeable clearJavaPlugin(Object instance) throws ReflectiveOperationException { + set(fields, instance, "loader", null); + set(fields, instance, "classLoader", null); + Class clazz = (Class) invoke(methods, instance, "getClass"); + if (clazz != null && clazz.getClassLoader() instanceof Closeable) { + return (Closeable) clazz.getClassLoader(); + } + return null; + } } diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java new file mode 100644 index 0000000..54ee88a --- /dev/null +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java @@ -0,0 +1,43 @@ +package net.frankheijden.serverutils.bukkit.reflection; + +import static net.frankheijden.serverutils.common.reflection.FieldParam.fieldOf; +import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.get; +import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.getAllFields; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; + +import org.bukkit.plugin.java.JavaPluginLoader; + +public class RJavaPluginLoader { + + private static Class javaPluginLoaderClass; + private static Map fields; + + static { + try { + javaPluginLoaderClass = JavaPluginLoader.class; + fields = getAllFields(javaPluginLoaderClass, + fieldOf("classes")); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Removes the given classes from the JavaPluginLoader instance. + * @param instance The instance. + * @param list The list of classpaths. + * @throws IllegalAccessException When prohibited access to the method. + */ + @SuppressWarnings("unchecked") + public static void removeClasses(Object instance, Collection list) throws IllegalAccessException { + Map> classes = (Map>) get(fields, instance, "classes"); + if (classes == null) return; + + for (String key : list) { + classes.remove(key); + } + } +} diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java index 6509d1d..4677649 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java @@ -1,10 +1,10 @@ package net.frankheijden.serverutils.bukkit.reflection; import static net.frankheijden.serverutils.common.reflection.FieldParam.fieldOf; +import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.get; import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.getAllFields; import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.set; -import java.io.Closeable; import java.lang.reflect.Field; import java.util.Map; @@ -18,7 +18,8 @@ public class RPluginClassLoader { pluginClassLoaderClass = Class.forName("org.bukkit.plugin.java.PluginClassLoader"); fields = getAllFields(pluginClassLoaderClass, fieldOf("plugin"), - fieldOf("pluginInit")); + fieldOf("pluginInit"), + fieldOf("classes")); } catch (Exception ex) { ex.printStackTrace(); } @@ -31,17 +32,13 @@ public class RPluginClassLoader { /** * Clears and closes the provided classloader. * @param loader The classloader instance. - * @return The Closeable object. * @throws IllegalAccessException When prohibited access to the field. */ - public static Closeable clearClassLoader(ClassLoader loader) throws IllegalAccessException { - if (loader == null) return null; + public static void clearClassLoader(ClassLoader loader) throws IllegalAccessException { + if (loader == null) return; if (isInstance(loader)) { - clearUrlClassLoader(loader); + clearPluginClassLoader(loader); } - - if (loader instanceof Closeable) return (Closeable) loader; - return null; } /** @@ -49,9 +46,14 @@ public class RPluginClassLoader { * @param pluginLoader The plugin loader instance. * @throws IllegalAccessException When prohibited access to the field. */ - public static void clearUrlClassLoader(Object pluginLoader) throws IllegalAccessException { + public static void clearPluginClassLoader(Object pluginLoader) throws IllegalAccessException { if (pluginLoader == null) return; set(fields, pluginLoader, "plugin", null); set(fields, pluginLoader, "pluginInit", null); } + + @SuppressWarnings("unchecked") + public static Map> getClasses(Object pluginLoader) throws IllegalAccessException { + return (Map>) get(fields, pluginLoader, "classes"); + } } diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/CommandServerUtils.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/CommandServerUtils.java index 814a7dd..c6003e3 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/CommandServerUtils.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/CommandServerUtils.java @@ -26,6 +26,7 @@ import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListFormat; +import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Command; @@ -136,6 +137,16 @@ public class CommandServerUtils extends BaseCommand { @CommandPermission("serverutils.reloadplugin") @Description("Reloads a specified plugin.") public void onReloadPlugin(CommandSender sender, String pluginName) { + if (pluginName.equalsIgnoreCase("ServerUtils")) { + String result = BungeePluginManager.get().reloadPlugin(pluginName).toString(); + if (result.equals("SUCCESS")) { + sender.sendMessage(ChatColor.GREEN + "Successfully reloaded ServerUtils."); + } else { + sender.sendMessage(ChatColor.RED + "Something went wrong reloading ServerUtils."); + } + return; + } + Result result = BungeePluginManager.get().reloadPlugin(pluginName); result.sendTo(BungeeUtils.wrap(sender), "reload", pluginName); } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java index 317b781..530b4ea 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java @@ -2,6 +2,8 @@ package net.frankheijden.serverutils.common.entities; import java.io.Closeable; import java.io.IOException; +import java.util.Collections; +import java.util.List; /** * A result which should be closed when done. @@ -9,18 +11,18 @@ import java.io.IOException; public class CloseableResult implements Closeable { private Result result; - private final Closeable closeable; + private final List closeables; /** * Constructs a new closable result. * Used for unloading / reloading a plugin. * NB: The closable needs to be closed to fully ensure that the old plugin doesn't work anymore! * @param result The result of the procedure - * @param closeable The closable of the procedure. + * @param closeables The list of closable's of the procedure. */ - public CloseableResult(Result result, Closeable closeable) { + public CloseableResult(Result result, List closeables) { this.result = result; - this.closeable = closeable; + this.closeables = closeables; } /** @@ -36,7 +38,15 @@ public class CloseableResult implements Closeable { * @param closeable The closable of the procedure. */ public CloseableResult(Closeable closeable) { - this(Result.SUCCESS, closeable); + this(Result.SUCCESS, Collections.singletonList(closeable)); + } + + /** + * Constructs a new closable result with a closable instance and success result. + * @param closeables The list of closable's of the procedure. + */ + public CloseableResult(List closeables) { + this(Result.SUCCESS, closeables); } /** @@ -61,7 +71,7 @@ public class CloseableResult implements Closeable { * Attempts to close the closable, essentially wrapping it with try-catch. */ public void tryClose() { - if (closeable == null) return; + if (closeables == null) return; try { close(); } catch (IOException ex) { @@ -75,6 +85,8 @@ public class CloseableResult implements Closeable { */ @Override public void close() throws IOException { - closeable.close(); + for (Closeable closeable : closeables) { + closeable.close(); + } } } diff --git a/build.gradle b/build.gradle index aa44db8..fda9a2b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'net.frankheijden.serverutils' -version = '2.0.4' +version = '2.0.5' sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8