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.
This commit is contained in:
Tastybento 2018-01-01 11:40:25 -08:00
parent df5434930c
commit 037bf04836
2 changed files with 99 additions and 3 deletions

View File

@ -5,6 +5,7 @@ import java.io.File;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.bukkit.plugin.InvalidDescriptionException; 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.AddonDescription.AddonDescriptionBuilder;
import us.tastybento.bskyblock.api.addons.exception.InvalidAddonFormatException; import us.tastybento.bskyblock.api.addons.exception.InvalidAddonFormatException;
import us.tastybento.bskyblock.api.addons.exception.InvalidAddonInheritException; import us.tastybento.bskyblock.api.addons.exception.InvalidAddonInheritException;
import us.tastybento.bskyblock.managers.AddonsManager;
/** /**
* @author Tastybento, ComminQ * @author Tastybento, ComminQ
*/ */
public class AddonClassLoader extends URLClassLoader { public class AddonClassLoader extends URLClassLoader {
private final Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
public Addon addon; public Addon addon;
private AddonsManager loader;
public AddonClassLoader(Map<String, String>data, File path, BufferedReader reader, ClassLoader loaders) throws InvalidAddonInheritException, MalformedURLException, InvalidAddonFormatException, InvalidDescriptionException { public AddonClassLoader(AddonsManager addonsManager, Map<String, String>data, File path, BufferedReader reader, ClassLoader parent) throws InvalidAddonInheritException, MalformedURLException, InvalidAddonFormatException, InvalidDescriptionException {
super(new URL[]{path.toURI().toURL()}, loaders); super(new URL[]{path.toURI().toURL()}, parent);
this.loader = addonsManager;
Addon addon = null; Addon addon = null;
@ -68,4 +74,46 @@ public class AddonClassLoader extends URLClassLoader {
.withAuthor(authors).build(); .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;
}
} }

View File

@ -13,6 +13,8 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.InvalidDescriptionException;
import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.BSkyBlock;
@ -30,6 +32,9 @@ public final class AddonsManager {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private List<Addon> addons; private List<Addon> addons;
private List<AddonClassLoader> loader; private List<AddonClassLoader> loader;
private final Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
public AddonsManager() { public AddonsManager() {
this.addons = new ArrayList<>(); this.addons = new ArrayList<>();
@ -114,7 +119,7 @@ public final class AddonsManager {
AddonClassLoader loader = null; 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); this.loader.add(loader);
addon = loader.addon; addon = loader.addon;
addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName())); addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName()));
@ -177,4 +182,47 @@ public final class AddonsManager {
this.loader = loader; 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<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
ConfigurationSerialization.registerClass(serializable);
}
}
}
} }