From 037bf048363a66f85e657e3daa6219c311fac225 Mon Sep 17 00:00:00 2001 From: Tastybento Date: Mon, 1 Jan 2018 11:40:25 -0800 Subject: [PATCH] Added methods to allow shared classes across multiple addons. The original code did not allow addons to talk to each other in any way. After trying loads of things with class path I studied the bukkit code and saw that they override the findClass method in URLClassLoader. This enables addons to find classes in other addons. All that was then needed was a map in the AddonsManager of classes that this manager knows about and a getter and setter method for them. After all that (3 hours) it works. --- .../api/addons/AddonClassLoader.java | 52 ++++++++++++++++++- .../bskyblock/managers/AddonsManager.java | 50 +++++++++++++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java b/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java index d94610cb1..e03df0d89 100644 --- a/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java +++ b/src/main/java/us/tastybento/bskyblock/api/addons/AddonClassLoader.java @@ -5,6 +5,7 @@ import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.util.HashMap; import java.util.Map; import org.bukkit.plugin.InvalidDescriptionException; @@ -13,16 +14,21 @@ import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.api.addons.AddonDescription.AddonDescriptionBuilder; import us.tastybento.bskyblock.api.addons.exception.InvalidAddonFormatException; import us.tastybento.bskyblock.api.addons.exception.InvalidAddonInheritException; +import us.tastybento.bskyblock.managers.AddonsManager; /** * @author Tastybento, ComminQ */ public class AddonClassLoader extends URLClassLoader { + private final Map> classes = new HashMap>(); public Addon addon; + private AddonsManager loader; - public AddonClassLoader(Mapdata, File path, BufferedReader reader, ClassLoader loaders) throws InvalidAddonInheritException, MalformedURLException, InvalidAddonFormatException, InvalidDescriptionException { - super(new URL[]{path.toURI().toURL()}, loaders); + public AddonClassLoader(AddonsManager addonsManager, Mapdata, File path, BufferedReader reader, ClassLoader parent) throws InvalidAddonInheritException, MalformedURLException, InvalidAddonFormatException, InvalidDescriptionException { + super(new URL[]{path.toURI().toURL()}, parent); + + this.loader = addonsManager; Addon addon = null; @@ -68,4 +74,46 @@ public class AddonClassLoader extends URLClassLoader { .withAuthor(authors).build(); } + + /* (non-Javadoc) + * @see java.net.URLClassLoader#findClass(java.lang.String) + */ + @Override + protected Class findClass(String name) throws ClassNotFoundException { + return findClass(name, true); + } + + /** + * This is a custom findClass that enables classes in other addons to be found + * (This code was copied from Bukkit's PluginLoader class + * @param name + * @param checkGlobal + * @return Class + * @throws ClassNotFoundException + */ + public Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { + if (name.startsWith("us.tastybento.")) { + throw new ClassNotFoundException(name); + } + Class result = classes.get(name); + + if (result == null) { + if (checkGlobal) { + result = loader.getClassByName(name); + } + + if (result == null) { + result = super.findClass(name); + + if (result != null) { + loader.setClass(name, result); + } + } + + classes.put(name, result); + } + + return result; + } + } diff --git a/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java b/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java index 26e743223..e55e7521d 100644 --- a/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java +++ b/src/main/java/us/tastybento/bskyblock/managers/AddonsManager.java @@ -13,6 +13,8 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.bukkit.Bukkit; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.plugin.InvalidDescriptionException; import us.tastybento.bskyblock.BSkyBlock; @@ -30,6 +32,9 @@ public final class AddonsManager { private static final boolean DEBUG = false; private List addons; private List loader; + private final Map> classes = new HashMap>(); + + public AddonsManager() { this.addons = new ArrayList<>(); @@ -114,7 +119,7 @@ public final class AddonsManager { AddonClassLoader loader = null; - loader = new AddonClassLoader(data, f, reader, this.getClass().getClassLoader()); + loader = new AddonClassLoader(this, data, f, reader, this.getClass().getClassLoader()); this.loader.add(loader); addon = loader.addon; addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName())); @@ -177,4 +182,47 @@ public final class AddonsManager { this.loader = loader; } + + /** + * Finds a class by name that has been loaded by this loader + * Code copied from Bukkit JavaPluginLoader + * @param name + * @return Class + */ + public Class getClassByName(final String name) { + Class cachedClass = classes.get(name); + + if (cachedClass != null) { + return cachedClass; + } else { + for (AddonClassLoader loader : loader) { + try { + cachedClass = loader.findClass(name, false); + } catch (ClassNotFoundException cnfe) {} + if (cachedClass != null) { + return cachedClass; + } + } + } + return null; + } + + /** + * Sets a class that this loader should know about + * Code copied from Bukkit JavaPluginLoader + * + * @param name + * @param clazz + */ + public void setClass(final String name, final Class clazz) { + if (!classes.containsKey(name)) { + classes.put(name, clazz); + + if (ConfigurationSerializable.class.isAssignableFrom(clazz)) { + Class serializable = clazz.asSubclass(ConfigurationSerializable.class); + ConfigurationSerialization.registerClass(serializable); + } + } + } + }