v2.0.5 - Fix for reloading ourselves

This commit is contained in:
Frank van der Heijden 2020-07-20 00:37:16 +02:00
parent 4b6008299b
commit 71b6a5fa02
No known key found for this signature in database
GPG key ID: 26DA56488D314D11
8 changed files with 147 additions and 21 deletions

View file

@ -32,6 +32,7 @@ import net.frankheijden.serverutils.common.utils.ForwardFilter;
import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder;
import net.frankheijden.serverutils.common.utils.ListFormat; import net.frankheijden.serverutils.common.utils.ListFormat;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginIdentifiableCommand; import org.bukkit.command.PluginIdentifiableCommand;
@ -195,6 +196,16 @@ public class CommandServerUtils extends BaseCommand {
@CommandPermission("serverutils.reloadplugin") @CommandPermission("serverutils.reloadplugin")
@Description("Reloads a specified plugin.") @Description("Reloads a specified plugin.")
public void onReloadPlugin(CommandSender sender, String pluginName) { 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 result = BukkitPluginManager.get().reloadPlugin(pluginName);
result.sendTo(BukkitUtils.wrap(sender), "reload", pluginName); result.sendTo(BukkitUtils.wrap(sender), "reload", pluginName);
} }

View file

@ -2,6 +2,8 @@ package net.frankheijden.serverutils.bukkit.managers;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; 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.RCraftServer;
import net.frankheijden.serverutils.bukkit.reflection.RCraftingManager; import net.frankheijden.serverutils.bukkit.reflection.RCraftingManager;
import net.frankheijden.serverutils.bukkit.reflection.RJavaPlugin; 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.RPluginClassLoader;
import net.frankheijden.serverutils.bukkit.reflection.RSimplePluginManager; import net.frankheijden.serverutils.bukkit.reflection.RSimplePluginManager;
import net.frankheijden.serverutils.common.entities.CloseableResult; import net.frankheijden.serverutils.common.entities.CloseableResult;
@ -136,16 +139,34 @@ public class BukkitPluginManager extends AbstractPluginManager<Plugin> {
@Override @Override
public CloseableResult unloadPlugin(Plugin plugin) { public CloseableResult unloadPlugin(Plugin plugin) {
if (plugin == null) return new CloseableResult(Result.NOT_EXISTS); if (plugin == null) return new CloseableResult(Result.NOT_EXISTS);
Closeable closeable; List<Closeable> closeables = new ArrayList<>();
try { try {
RSimplePluginManager.getPlugins(Bukkit.getPluginManager()).remove(plugin); RSimplePluginManager.getPlugins(Bukkit.getPluginManager()).remove(plugin);
RSimplePluginManager.removeLookupName(Bukkit.getPluginManager(), plugin.getName()); 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<String, Class<?>> 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) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
return new CloseableResult(Result.ERROR); return new CloseableResult(Result.ERROR);
} }
return new CloseableResult(closeable); return new CloseableResult(closeables);
}
private static void addIfInstance(List<Closeable> list, Object obj) {
if (obj instanceof Closeable) {
list.add((Closeable) obj);
}
} }
/** /**

View file

@ -1,10 +1,15 @@
package net.frankheijden.serverutils.bukkit.reflection; 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.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.getAllMethods;
import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.invoke; 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.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
@ -14,13 +19,18 @@ import org.bukkit.plugin.java.JavaPlugin;
public class RJavaPlugin { public class RJavaPlugin {
private static Class<?> javaPluginClass; private static Class<?> javaPluginClass;
private static Map<String, Field> fields;
private static Map<String, Method> methods; private static Map<String, Method> methods;
static { static {
try { try {
javaPluginClass = JavaPlugin.class; javaPluginClass = JavaPlugin.class;
fields = getAllFields(javaPluginClass,
fieldOf("loader"),
fieldOf("classLoader"));
methods = getAllMethods(javaPluginClass, methods = getAllMethods(javaPluginClass,
methodOf("getClassLoader"), methodOf("getClassLoader"),
methodOf("getClass"),
methodOf("getFile")); methodOf("getFile"));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
@ -34,4 +44,20 @@ public class RJavaPlugin {
public static File getFile(Object instance) throws ReflectiveOperationException { public static File getFile(Object instance) throws ReflectiveOperationException {
return (File) invoke(methods, instance, "getFile"); 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;
}
} }

View file

@ -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<String, Field> 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<? extends String> list) throws IllegalAccessException {
Map<String, Class<?>> classes = (Map<String, Class<?>>) get(fields, instance, "classes");
if (classes == null) return;
for (String key : list) {
classes.remove(key);
}
}
}

View file

@ -1,10 +1,10 @@
package net.frankheijden.serverutils.bukkit.reflection; package net.frankheijden.serverutils.bukkit.reflection;
import static net.frankheijden.serverutils.common.reflection.FieldParam.fieldOf; 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.getAllFields;
import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.set; import static net.frankheijden.serverutils.common.reflection.ReflectionUtils.set;
import java.io.Closeable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Map; import java.util.Map;
@ -18,7 +18,8 @@ public class RPluginClassLoader {
pluginClassLoaderClass = Class.forName("org.bukkit.plugin.java.PluginClassLoader"); pluginClassLoaderClass = Class.forName("org.bukkit.plugin.java.PluginClassLoader");
fields = getAllFields(pluginClassLoaderClass, fields = getAllFields(pluginClassLoaderClass,
fieldOf("plugin"), fieldOf("plugin"),
fieldOf("pluginInit")); fieldOf("pluginInit"),
fieldOf("classes"));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
@ -31,17 +32,13 @@ public class RPluginClassLoader {
/** /**
* Clears and closes the provided classloader. * Clears and closes the provided classloader.
* @param loader The classloader instance. * @param loader The classloader instance.
* @return The Closeable object.
* @throws IllegalAccessException When prohibited access to the field. * @throws IllegalAccessException When prohibited access to the field.
*/ */
public static Closeable clearClassLoader(ClassLoader loader) throws IllegalAccessException { public static void clearClassLoader(ClassLoader loader) throws IllegalAccessException {
if (loader == null) return null; if (loader == null) return;
if (isInstance(loader)) { 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. * @param pluginLoader The plugin loader instance.
* @throws IllegalAccessException When prohibited access to the field. * @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; if (pluginLoader == null) return;
set(fields, pluginLoader, "plugin", null); set(fields, pluginLoader, "plugin", null);
set(fields, pluginLoader, "pluginInit", null); set(fields, pluginLoader, "pluginInit", null);
} }
@SuppressWarnings("unchecked")
public static Map<String, Class<?>> getClasses(Object pluginLoader) throws IllegalAccessException {
return (Map<String,Class<?>>) get(fields, pluginLoader, "classes");
}
} }

View file

@ -26,6 +26,7 @@ import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.FormatBuilder;
import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder;
import net.frankheijden.serverutils.common.utils.ListFormat; 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.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Command;
@ -136,6 +137,16 @@ public class CommandServerUtils extends BaseCommand {
@CommandPermission("serverutils.reloadplugin") @CommandPermission("serverutils.reloadplugin")
@Description("Reloads a specified plugin.") @Description("Reloads a specified plugin.")
public void onReloadPlugin(CommandSender sender, String pluginName) { 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 result = BungeePluginManager.get().reloadPlugin(pluginName);
result.sendTo(BungeeUtils.wrap(sender), "reload", pluginName); result.sendTo(BungeeUtils.wrap(sender), "reload", pluginName);
} }

View file

@ -2,6 +2,8 @@ package net.frankheijden.serverutils.common.entities;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List;
/** /**
* A result which should be closed when done. * A result which should be closed when done.
@ -9,18 +11,18 @@ import java.io.IOException;
public class CloseableResult implements Closeable { public class CloseableResult implements Closeable {
private Result result; private Result result;
private final Closeable closeable; private final List<Closeable> closeables;
/** /**
* Constructs a new closable result. * Constructs a new closable result.
* Used for unloading / reloading a plugin. * Used for unloading / reloading a plugin.
* NB: The closable needs to be closed to fully ensure that the old plugin doesn't work anymore! * 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 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<Closeable> closeables) {
this.result = result; 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. * @param closeable The closable of the procedure.
*/ */
public CloseableResult(Closeable closeable) { 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<Closeable> 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. * Attempts to close the closable, essentially wrapping it with try-catch.
*/ */
public void tryClose() { public void tryClose() {
if (closeable == null) return; if (closeables == null) return;
try { try {
close(); close();
} catch (IOException ex) { } catch (IOException ex) {
@ -75,6 +85,8 @@ public class CloseableResult implements Closeable {
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
closeable.close(); for (Closeable closeable : closeables) {
closeable.close();
}
} }
} }

View file

@ -4,7 +4,7 @@ plugins {
} }
group = 'net.frankheijden.serverutils' group = 'net.frankheijden.serverutils'
version = '2.0.4' version = '2.0.5'
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8