From f8984c4486582ee5794f8dda8003d7aca6bf5be5 Mon Sep 17 00:00:00 2001 From: Wesley Wolfe Date: Tue, 10 Dec 2013 18:50:58 -0600 Subject: [BREAKING] Shift plugin initialization; Addresses BUKKIT-1788 This reverts commit ae4f1c05d825e232d7fc0483639ba65ad54d2db4, restoring commit 27cb5e7c9c6b2cfc5419262df75d89bc6bfe7879 (mostly). Shared class loading was removed as an explicit feature in the plugin.yml, as all plugins implicitly share class loaders already. Some deprecated, internal functionality is now (package) private, namely some sections pointed out in 203de4180b40f069d2c175d763476bd4ce338c76. --- .../org/bukkit/plugin/PluginDescriptionFile.java | 4 + .../java/org/bukkit/plugin/java/JavaPlugin.java | 120 ++++++++------ .../org/bukkit/plugin/java/JavaPluginLoader.java | 173 ++++----------------- .../org/bukkit/plugin/java/PluginClassLoader.java | 116 +++++++------- 4 files changed, 165 insertions(+), 248 deletions(-) (limited to 'src/main') diff --git a/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java b/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java index d2a59e40..1d79e637 100644 --- a/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java +++ b/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java @@ -773,6 +773,10 @@ public final class PluginDescriptionFile { return name + " v" + version; } + /** + * @deprecated unused + */ + @Deprecated public String getClassLoaderOf() { return classLoaderOf; } diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java index 3815da22..bdecbea2 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java @@ -13,12 +13,14 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.bukkit.Server; +import org.bukkit.Warning.WarningState; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.AuthorNagException; import org.bukkit.plugin.PluginBase; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginLoader; @@ -36,7 +38,6 @@ import com.avaje.ebeaninternal.server.ddl.DdlGenerator; */ public abstract class JavaPlugin extends PluginBase { private boolean isEnabled = false; - private boolean initialized = false; private PluginLoader loader = null; private Server server = null; private File file = null; @@ -49,7 +50,37 @@ public abstract class JavaPlugin extends PluginBase { private File configFile = null; private PluginLogger logger = null; - public JavaPlugin() {} + public JavaPlugin() { + final ClassLoader classLoader = this.getClass().getClassLoader(); + if (!(classLoader instanceof PluginClassLoader)) { + throw new IllegalStateException("JavaPlugin requires " + PluginClassLoader.class.getName()); + } + ((PluginClassLoader) classLoader).initialize(this); + } + + /** + * @deprecated This method is intended for unit testing purposes when the + * other {@linkplain #JavaPlugin(JavaPluginLoader, + * PluginDescriptionFile, File, File) constructor} cannot be used. + *

+ * Its existence may be temporary. + */ + @Deprecated + protected JavaPlugin(final PluginLoader loader, final Server server, final PluginDescriptionFile description, final File dataFolder, final File file) { + final ClassLoader classLoader = this.getClass().getClassLoader(); + if (classLoader instanceof PluginClassLoader) { + throw new IllegalStateException("Cannot use initialization constructor at runtime"); + } + init(loader, server, description, dataFolder, file, classLoader); + } + + protected JavaPlugin(final JavaPluginLoader loader, final PluginDescriptionFile description, final File dataFolder, final File file) { + final ClassLoader classLoader = this.getClass().getClassLoader(); + if (classLoader instanceof PluginClassLoader) { + throw new IllegalStateException("Cannot use initialization constructor at runtime"); + } + init(loader, loader.server, description, dataFolder, file, classLoader); + } /** * Returns the folder that the plugin data's files are located in. The @@ -223,50 +254,46 @@ public abstract class JavaPlugin extends PluginBase { } /** - * Initializes this plugin with the given variables. - *

- * This method should never be called manually. - * - * @param loader PluginLoader that is responsible for this plugin - * @param server Server instance that is running this plugin - * @param description PluginDescriptionFile containing metadata on this - * plugin - * @param dataFolder Folder containing the plugin's data - * @param file File containing this plugin - * @param classLoader ClassLoader which holds this plugin + * @deprecated This method is legacy and will be removed - it must be + * replaced by the specially provided constructor(s). */ + @Deprecated protected final void initialize(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) { - if (!initialized) { - this.initialized = true; - this.loader = loader; - this.server = server; - this.file = file; - this.description = description; - this.dataFolder = dataFolder; - this.classLoader = classLoader; - this.configFile = new File(dataFolder, "config.yml"); - this.logger = new PluginLogger(this); - - if (description.isDatabaseEnabled()) { - ServerConfig db = new ServerConfig(); - - db.setDefaultServer(false); - db.setRegister(false); - db.setClasses(getDatabaseClasses()); - db.setName(description.getName()); - server.configureDbConfig(db); - - DataSourceConfig ds = db.getDataSourceConfig(); - - ds.setUrl(replaceDatabaseString(ds.getUrl())); - dataFolder.mkdirs(); - - ClassLoader previous = Thread.currentThread().getContextClassLoader(); - - Thread.currentThread().setContextClassLoader(classLoader); - ebean = EbeanServerFactory.create(db); - Thread.currentThread().setContextClassLoader(previous); - } + if (server.getWarningState() == WarningState.OFF) { + return; + } + getLogger().log(Level.WARNING, getClass().getName() + " is already initialized", server.getWarningState() == WarningState.DEFAULT ? null : new AuthorNagException("Explicit initialization")); + } + + final void init(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) { + this.loader = loader; + this.server = server; + this.file = file; + this.description = description; + this.dataFolder = dataFolder; + this.classLoader = classLoader; + this.configFile = new File(dataFolder, "config.yml"); + this.logger = new PluginLogger(this); + + if (description.isDatabaseEnabled()) { + ServerConfig db = new ServerConfig(); + + db.setDefaultServer(false); + db.setRegister(false); + db.setClasses(getDatabaseClasses()); + db.setName(description.getName()); + server.configureDbConfig(db); + + DataSourceConfig ds = db.getDataSourceConfig(); + + ds.setUrl(replaceDatabaseString(ds.getUrl())); + dataFolder.mkdirs(); + + ClassLoader previous = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader(classLoader); + ebean = EbeanServerFactory.create(db); + Thread.currentThread().setContextClassLoader(previous); } } @@ -289,9 +316,12 @@ public abstract class JavaPlugin extends PluginBase { * Gets the initialization status of this plugin * * @return true if this plugin is initialized, otherwise false + * @deprecated This method cannot return false, as {@link + * JavaPlugin} is now initialized in the constructor. */ + @Deprecated public final boolean isInitialized() { - return initialized; + return true; } /** diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java index ea30d83d..57681dc4 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -4,10 +4,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -49,44 +47,19 @@ import com.google.common.collect.ImmutableList; /** * Represents a Java plugin loader, allowing plugins in the form of .jar */ -public class JavaPluginLoader implements PluginLoader { +public final class JavaPluginLoader implements PluginLoader { final Server server; - final boolean extended = this.getClass() != JavaPluginLoader.class; - boolean warn; + private final Pattern[] fileFilters = new Pattern[] { Pattern.compile("\\.jar$"), }; + private final Map> classes = new HashMap>(); + private final Map loaders = new LinkedHashMap(); - private final Pattern[] fileFilters0 = new Pattern[] { Pattern.compile("\\.jar$"), }; /** - * @deprecated Internal field that wasn't intended to be exposed - */ - @Deprecated - protected final Pattern[] fileFilters = fileFilters0; - - private final Map> classes0 = new HashMap>(); - /** - * @deprecated Internal field that wasn't intended to be exposed - */ - @Deprecated - protected final Map> classes = classes0; - - private final Map loaders0 = new LinkedHashMap(); - /** - * @deprecated Internal field that wasn't intended to be exposed - */ - @Deprecated - protected final Map loaders = loaders0; - - /** - * This class was not meant to be extended + * This class was not meant to be constructed explicitly */ @Deprecated public JavaPluginLoader(Server instance) { Validate.notNull(instance, "Server cannot be null"); server = instance; - warn = instance.getWarningState() != WarningState.OFF; - if (extended && warn) { - warn = false; - instance.getLogger().log(Level.WARNING, "JavaPluginLoader not intended to be extended by " + getClass() + ", and may be final in a future version of Bukkit"); - } } public Plugin loadPlugin(File file) throws InvalidPluginException { @@ -104,7 +77,7 @@ public class JavaPluginLoader implements PluginLoader { } File dataFolder = new File(file.getParentFile(), description.getName()); - File oldDataFolder = extended ? getDataFolder(file) : getDataFolder0(file); // Don't warn on deprecation, but maintain overridability + File oldDataFolder = getDataFolder(file); // Found old data folder if (dataFolder.equals(oldDataFolder)) { @@ -145,75 +118,31 @@ public class JavaPluginLoader implements PluginLoader { } for (String pluginName : depend) { - if (loaders0 == null) { + if (loaders == null) { throw new UnknownDependencyException(pluginName); } - PluginClassLoader current = loaders0.get(pluginName); + PluginClassLoader current = loaders.get(pluginName); if (current == null) { throw new UnknownDependencyException(pluginName); } } - PluginClassLoader loader = null; - JavaPlugin result = null; - + PluginClassLoader loader; try { - URL[] urls = new URL[1]; - - urls[0] = file.toURI().toURL(); - - if (description.getClassLoaderOf() != null) { - loader = loaders0.get(description.getClassLoaderOf()); - loader.addURL(urls[0]); - } else { - loader = new PluginClassLoader(this, urls, getClass().getClassLoader(), null); - } - - Class jarClass = Class.forName(description.getMain(), true, loader); - Class plugin = jarClass.asSubclass(JavaPlugin.class); - - Constructor constructor = plugin.getConstructor(); - - result = constructor.newInstance(); - - result.initialize(this, server, description, dataFolder, file, loader); - } catch (InvocationTargetException ex) { - throw new InvalidPluginException(ex.getCause()); + loader = new PluginClassLoader(this, getClass().getClassLoader(), description, dataFolder, file); + } catch (InvalidPluginException ex) { + throw ex; } catch (Throwable ex) { throw new InvalidPluginException(ex); } - loaders0.put(description.getName(), loader); + loaders.put(description.getName(), loader); - return result; + return loader.plugin; } - /** - * @deprecated Relic method from PluginLoader that didn't get purged - */ - @Deprecated - public Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException { - if (warn) { - server.getLogger().log(Level.WARNING, "Method \"public Plugin loadPlugin(File, boolean)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - warn = false; - } - return loadPlugin(file); - } - - /** - * @deprecated Internal method that wasn't intended to be exposed - */ - @Deprecated - protected File getDataFolder(File file) { - if (warn) { - server.getLogger().log(Level.WARNING, "Method \"protected File getDataFolder(File)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - warn = false; - } - return getDataFolder0(file); - } - - private File getDataFolder0(File file) { + private File getDataFolder(File file) { File dataFolder = null; String filename = file.getName(); @@ -272,32 +201,20 @@ public class JavaPluginLoader implements PluginLoader { } public Pattern[] getPluginFileFilters() { - return fileFilters0.clone(); - } - - /** - * @deprecated Internal method that wasn't intended to be exposed - */ - @Deprecated - public Class getClassByName(final String name) { - if (warn) { - server.getLogger().log(Level.WARNING, "Method \"public Class getClassByName(String)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - warn = false; - } - return getClassByName0(name); + return fileFilters.clone(); } - Class getClassByName0(final String name) { - Class cachedClass = classes0.get(name); + Class getClassByName(final String name) { + Class cachedClass = classes.get(name); if (cachedClass != null) { return cachedClass; } else { - for (String current : loaders0.keySet()) { - PluginClassLoader loader = loaders0.get(current); + for (String current : loaders.keySet()) { + PluginClassLoader loader = loaders.get(current); try { - cachedClass = loader.extended ? loader.findClass(name, false) : loader.findClass0(name, false); // Don't warn on deprecation, but maintain overridability + cachedClass = loader.findClass(name, false); } catch (ClassNotFoundException cnfe) {} if (cachedClass != null) { return cachedClass; @@ -307,21 +224,9 @@ public class JavaPluginLoader implements PluginLoader { return null; } - /** - * @deprecated Internal method that wasn't intended to be exposed - */ - @Deprecated - public void setClass(final String name, final Class clazz) { - if (warn) { - server.getLogger().log(Level.WARNING, "Method \"public void setClass(String, Class)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - warn = false; - } - setClass0(name, clazz); - } - - void setClass0(final String name, final Class clazz) { - if (!classes0.containsKey(name)) { - classes0.put(name, clazz); + 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); @@ -330,20 +235,8 @@ public class JavaPluginLoader implements PluginLoader { } } - /** - * @deprecated Internal method that wasn't intended to be exposed - */ - @Deprecated - public void removeClass(String name) { - if (warn) { - server.getLogger().log(Level.WARNING, "Method \"public void removeClass(String)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - warn = false; - } - removeClass0(name); - } - - private void removeClass0(String name) { - Class clazz = classes0.remove(name); + private void removeClass(String name) { + Class clazz = classes.remove(name); try { if ((clazz != null) && (ConfigurationSerializable.class.isAssignableFrom(clazz))) { @@ -449,8 +342,8 @@ public class JavaPluginLoader implements PluginLoader { String pluginName = jPlugin.getDescription().getName(); - if (!loaders0.containsKey(pluginName)) { - loaders0.put(pluginName, (PluginClassLoader) jPlugin.getClassLoader()); + if (!loaders.containsKey(pluginName)) { + loaders.put(pluginName, (PluginClassLoader) jPlugin.getClassLoader()); } try { @@ -483,18 +376,14 @@ public class JavaPluginLoader implements PluginLoader { server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); } - loaders0.remove(jPlugin.getDescription().getName()); + loaders.remove(jPlugin.getDescription().getName()); if (cloader instanceof PluginClassLoader) { PluginClassLoader loader = (PluginClassLoader) cloader; - Set names = loader.extended ? loader.getClasses() : loader.getClasses0(); // Don't warn on deprecation, but maintain overridability + Set names = loader.getClasses(); for (String name : names) { - if (extended) { - removeClass(name); - } else { - removeClass0(name); - } + removeClass(name); } } } diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java index 29ec3fc4..c34fb2dc 100644 --- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java @@ -1,71 +1,68 @@ package org.bukkit.plugin.java; +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 java.util.Set; -import java.util.logging.Level; import org.apache.commons.lang.Validate; -import org.bukkit.plugin.AuthorNagException; +import org.bukkit.plugin.InvalidPluginException; +import org.bukkit.plugin.PluginDescriptionFile; /** * A ClassLoader for plugins, to allow shared classes across multiple plugins */ -public class PluginClassLoader extends URLClassLoader { +final class PluginClassLoader extends URLClassLoader { private final JavaPluginLoader loader; private final Map> classes = new HashMap>(); - final boolean extended = this.getClass() != PluginClassLoader.class; - - /** - * Internal class not intended to be exposed - */ - @Deprecated - public PluginClassLoader(final JavaPluginLoader loader, final URL[] urls, final ClassLoader parent) { - this(loader, urls, parent, null); - - if (loader.warn) { - if (extended) { - loader.server.getLogger().log(Level.WARNING, "PluginClassLoader not intended to be extended by " + getClass() + ", and may be final in a future version of Bukkit"); - } else { - loader.server.getLogger().log(Level.WARNING, "Constructor \"public PluginClassLoader(JavaPluginLoader, URL[], ClassLoader)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - } - loader.warn = false; - } - } - - - PluginClassLoader(final JavaPluginLoader loader, final URL[] urls, final ClassLoader parent, final Object methodSignature) { - super(urls, parent); + private final PluginDescriptionFile description; + private final File dataFolder; + private final File file; + final JavaPlugin plugin; + private JavaPlugin pluginInit; + private IllegalStateException pluginState; + + PluginClassLoader(final JavaPluginLoader loader, final ClassLoader parent, final PluginDescriptionFile description, final File dataFolder, final File file) throws InvalidPluginException, MalformedURLException { + super(new URL[] {file.toURI().toURL()}, parent); Validate.notNull(loader, "Loader cannot be null"); this.loader = loader; - } + this.description = description; + this.dataFolder = dataFolder; + this.file = file; + + try { + Class jarClass; + try { + jarClass = Class.forName(description.getMain(), true, this); + } catch (ClassNotFoundException ex) { + throw new InvalidPluginException("Cannot find main class `" + description.getMain() + "'", ex); + } - @Override - public void addURL(URL url) { // Override for access level! - super.addURL(url); + Class pluginClass; + try { + pluginClass = jarClass.asSubclass(JavaPlugin.class); + } catch (ClassCastException ex) { + throw new InvalidPluginException("main class `" + description.getMain() + "' does not extend JavaPlugin", ex); + } + + plugin = pluginClass.newInstance(); + } catch (IllegalAccessException ex) { + throw new InvalidPluginException("No public constructor", ex); + } catch (InstantiationException ex) { + throw new InvalidPluginException("Abnormal plugin type", ex); + } } @Override protected Class findClass(String name) throws ClassNotFoundException { - return extended ? findClass(name, true) : findClass0(name, true); // Don't warn on deprecation, but maintain overridability + return findClass(name, true); } - /** - * @deprecated Internal method that wasn't intended to be exposed - */ - @Deprecated - protected Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { - if (loader.warn) { - loader.server.getLogger().log(Level.WARNING, "Method \"protected Class findClass(String, boolean)\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - loader.warn = false; - } - return findClass0(name, checkGlobal); - } - - Class findClass0(String name, boolean checkGlobal) throws ClassNotFoundException { + Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { throw new ClassNotFoundException(name); } @@ -73,18 +70,14 @@ public class PluginClassLoader extends URLClassLoader { if (result == null) { if (checkGlobal) { - result = loader.extended ? loader.getClassByName(name) : loader.getClassByName0(name); // Don't warn on deprecation, but maintain overridability + result = loader.getClassByName(name); } if (result == null) { result = super.findClass(name); if (result != null) { - if (loader.extended) { // Don't warn on deprecation, but maintain overridability - loader.setClass(name, result); - } else { - loader.setClass0(name, result); - } + loader.setClass(name, result); } } @@ -94,19 +87,20 @@ public class PluginClassLoader extends URLClassLoader { return result; } - /** - * @deprecated Internal method that wasn't intended to be exposed - */ - @Deprecated - public Set getClasses() { - if (loader.warn) { - loader.server.getLogger().log(Level.WARNING, "Method \"public Set getClasses()\" is Deprecated, and may be removed in a future version of Bukkit", new AuthorNagException("")); - loader.warn = false; - } - return getClasses0(); + Set getClasses() { + return classes.keySet(); } - Set getClasses0() { - return classes.keySet(); + synchronized void initialize(JavaPlugin javaPlugin) { + Validate.notNull(javaPlugin, "Initializing plugin cannot be null"); + Validate.isTrue(javaPlugin.getClass().getClassLoader() == this, "Cannot initialize plugin outside of this class loader"); + if (this.plugin != null || this.pluginInit != null) { + throw new IllegalArgumentException("Plugin already intialized!", pluginState); + } + + pluginState = new IllegalStateException("Initial initialization"); + this.pluginInit = javaPlugin; + + javaPlugin.init(loader, loader.server, description, dataFolder, file, this); } } -- cgit v1.2.3