From d7469ca79e0da1e75bbb6fdfa631b6f17df1b3ac Mon Sep 17 00:00:00 2001 From: EvilSeph Date: Wed, 25 Jan 2012 04:11:37 -0500 Subject: Fixed plugin loader. Thanks Wolvereness! --- src/main/java/org/bukkit/plugin/PluginLoader.java | 12 + .../org/bukkit/plugin/SimplePluginManager.java | 242 ++++++++++++++++----- .../org/bukkit/plugin/java/JavaPluginLoader.java | 96 ++++---- 3 files changed, 243 insertions(+), 107 deletions(-) diff --git a/src/main/java/org/bukkit/plugin/PluginLoader.java b/src/main/java/org/bukkit/plugin/PluginLoader.java index 2a9dc10f..141311ab 100644 --- a/src/main/java/org/bukkit/plugin/PluginLoader.java +++ b/src/main/java/org/bukkit/plugin/PluginLoader.java @@ -29,6 +29,7 @@ public interface PluginLoader { /** * Loads the plugin contained in the specified file * + * @deprecated SoftDependencies are always ignored * @param file File to attempt to load * @param ignoreSoftDependencies Loader will ignore soft dependencies if this flag is set to true * @return Plugin that was contained in the specified file, or null if @@ -37,8 +38,19 @@ public interface PluginLoader { * @throws InvalidDescriptionException If the plugin description file was invalid * @throws UnknownDependencyException If a required dependency could not be found */ + @Deprecated public Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException; + /** + * Loads a PluginDescriptionFile from the specified file + * + * @param file File to attempt to load from + * @return A new PluginDescriptionFile loaded from the plugin.yml in the specified file + * @throws InvalidPluginException If when the specified file does not contain a plugin description file + * @throws InvalidDescriptionException If the plugin description file could not be created + */ + public PluginDescriptionFile getPluginDescription(File file) throws InvalidPluginException, InvalidDescriptionException; + /** * Returns a list of all filename filters expected by this PluginLoader * diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java index b6fbe2eb..cc99bf83 100644 --- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -8,6 +8,7 @@ import java.util.*; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang.Validate; import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.PluginCommandYamlParser; @@ -85,54 +86,170 @@ public final class SimplePluginManager implements PluginManager { * @return A list of all plugins loaded */ public Plugin[] loadPlugins(File directory) { - List result = new ArrayList(); - File[] files = directory.listFiles(); - - boolean allFailed = false; - boolean finalPass = false; + Validate.notNull(directory, "Directory cannot be null"); + Validate.isTrue(directory.isDirectory(), "Directory must be a directory"); - LinkedList filesList = new LinkedList(Arrays.asList(files)); + List result = new ArrayList(); + Set filters = fileAssociations.keySet(); if (!(server.getUpdateFolder().equals(""))) { updateDirectory = new File(directory, server.getUpdateFolder()); } - while (!allFailed || finalPass) { - allFailed = true; - Iterator itr = filesList.iterator(); + Map plugins = new HashMap(); + Set loadedPlugins = new HashSet(); + Map> dependencies = new HashMap>(); + Map> softDependencies = new HashMap>(); - while (itr.hasNext()) { - File file = itr.next(); - Plugin plugin = null; + // This is where it figures out all possible plugins + for (File file : directory.listFiles()) { + PluginLoader loader = null; + for (Pattern filter : filters) { + Matcher match = filter.matcher(file.getName()); + if (match.find()) { + loader = fileAssociations.get(filter); + } + } - try { - plugin = loadPlugin(file, finalPass); - } catch (UnknownDependencyException ex) { - if (finalPass) { - server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); - itr.remove(); - } else { - plugin = null; + if (loader == null) continue; + + PluginDescriptionFile description = null; + try { + description = loader.getPluginDescription(file); + } catch (InvalidPluginException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); + continue; + } catch (InvalidDescriptionException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); + continue; + } + + plugins.put(description.getName(), file); + + Collection softDependencySet = (Collection) description.getSoftDepend(); + if (softDependencySet != null) { + softDependencies.put(description.getName(), new LinkedList(softDependencySet)); + } + + Collection dependencySet = (Collection) description.getDepend(); + if (dependencySet != null) { + dependencies.put(description.getName(), new LinkedList(dependencySet)); + } + } + + while (!plugins.isEmpty()) { + boolean missingDependency = true; + Iterator pluginIterator = plugins.keySet().iterator(); + + while (pluginIterator.hasNext()) { + String plugin = pluginIterator.next(); + + if (dependencies.containsKey(plugin)) { + Iterator dependencyIterator = dependencies.get(plugin).iterator(); + + while (dependencyIterator.hasNext()) { + String dependency = dependencyIterator.next(); + + // Dependency loaded + if (loadedPlugins.contains(dependency)) { + dependencyIterator.remove(); + + // We have a dependency not found + } else if (!plugins.containsKey(dependency)) { + missingDependency = false; + File file = plugins.get(plugin); + pluginIterator.remove(); + softDependencies.remove(plugin); + dependencies.remove(plugin); + + server.getLogger().log( + Level.SEVERE, + "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", + new UnknownDependencyException(dependency)); + break; + } + } + + if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) { + dependencies.remove(plugin); + } + } + if (softDependencies.containsKey(plugin)) { + Iterator softDependencyIterator = softDependencies.get(plugin).iterator(); + + while (softDependencyIterator.hasNext()) { + String softDependency = softDependencyIterator.next(); + + // Soft depend is no longer around + if (!plugins.containsKey(softDependency)) { + softDependencyIterator.remove(); + } + } + + if (softDependencies.get(plugin).isEmpty()) { + softDependencies.remove(plugin); } - } catch (InvalidPluginException ex) { - server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", ex.getCause()); - itr.remove(); - } catch (InvalidDescriptionException ex) { - server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); - itr.remove(); } + if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) { + // We're clear to load, no more soft or hard dependencies left + File file = plugins.get(plugin); + pluginIterator.remove(); + missingDependency = false; - if (plugin != null) { - result.add(plugin); - allFailed = false; - finalPass = false; - itr.remove(); + try { + result.add(loadPlugin(file)); + loadedPlugins.add(plugin); + continue; + } catch (InvalidPluginException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", ex.getCause()); + } catch (InvalidDescriptionException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); + } catch (UnknownDependencyException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); + } } } - if (finalPass) { - break; - } else if (allFailed) { - finalPass = true; + + if (missingDependency) { + // We now iterate over plugins until something loads + // This loop will ignore soft dependencies + pluginIterator = plugins.keySet().iterator(); + + while (pluginIterator.hasNext()) { + String plugin = pluginIterator.next(); + + if (!dependencies.containsKey(plugin)) { + softDependencies.remove(plugin); + dependencies.remove(plugin); + missingDependency = false; + File file = plugins.get(plugin); + pluginIterator.remove(); + + try { + result.add(loadPlugin(file)); + loadedPlugins.add(plugin); + break; + } catch (InvalidPluginException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", ex.getCause()); + } catch (InvalidDescriptionException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); + } catch (UnknownDependencyException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex); + } + } + } + // We have no plugins left without a depend + if (missingDependency) { + softDependencies.clear(); + dependencies.clear(); + Iterator failedPluginIterator = plugins.values().iterator(); + + while (failedPluginIterator.hasNext()) { + File file = failedPluginIterator.next(); + failedPluginIterator.remove(); + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected"); + } + } } } @@ -151,29 +268,9 @@ public final class SimplePluginManager implements PluginManager { * @throws UnknownDependencyException If a required dependency could not be found */ public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException { - return loadPlugin(file, true); - } + Validate.notNull(file, "File cannot be null"); - /** - * Loads the plugin in the specified file - *

- * File must be valid according to the current enabled Plugin interfaces - * - * @param file File containing the plugin to load - * @param ignoreSoftDependencies Loader will ignore soft dependencies if this flag is set to true - * @return The Plugin loaded, or null if it was invalid - * @throws InvalidPluginException Thrown when the specified file is not a valid plugin - * @throws InvalidDescriptionException Thrown when the specified file contains an invalid description - * @throws UnknownDependencyException If a required dependency could not be found - */ - public synchronized Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException { - File updateFile = null; - - if (updateDirectory != null && updateDirectory.isDirectory() && (updateFile = new File(updateDirectory, file.getName())).isFile()) { - if (FileUtil.copy(updateFile, file)) { - updateFile.delete(); - } - } + checkUpdate(file); Set filters = fileAssociations.keySet(); Plugin result = null; @@ -185,7 +282,7 @@ public final class SimplePluginManager implements PluginManager { if (match.find()) { PluginLoader loader = fileAssociations.get(filter); - result = loader.loadPlugin(file, ignoreSoftDependencies); + result = loader.loadPlugin(file); } } @@ -197,6 +294,35 @@ public final class SimplePluginManager implements PluginManager { return result; } + private void checkUpdate(File file) { + if (updateDirectory == null || !updateDirectory.isDirectory()) { + return; + } + + File updateFile = new File(updateDirectory, file.getName()); + if (updateFile.isFile() && FileUtil.copy(updateFile, file)) { + updateFile.delete(); + } + } + + /** + * Loads the plugin in the specified file + *

+ * File must be valid according to the current enabled Plugin interfaces + * + * @deprecated soft-dependencies are now ignored + * @param file File containing the plugin to load + * @param ignoreSoftDependencies Loader will ignore soft dependencies if this flag is set to true + * @return The Plugin loaded, or null if it was invalid + * @throws InvalidPluginException Thrown when the specified file is not a valid plugin + * @throws InvalidDescriptionException Thrown when the specified file contains an invalid description + * @throws UnknownDependencyException If a required dependency could not be found + */ + @Deprecated + public synchronized Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException { + return loadPlugin(file); + } + /** * Checks if the given plugin is loaded and returns it when applicable *

diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java index 2099fbda..4fb8e31a 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -13,6 +13,8 @@ import java.util.jar.JarFile; import java.util.logging.Level; import java.util.regex.Pattern; +import org.apache.commons.lang.Validate; + import org.bukkit.Bukkit; import org.bukkit.Server; @@ -45,36 +47,13 @@ public class JavaPluginLoader implements PluginLoader { } public Plugin loadPlugin(File file) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException { - return loadPlugin(file, false); - } - - @SuppressWarnings("unchecked") - public Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException { - JavaPlugin result = null; - PluginDescriptionFile description = null; + Validate.notNull(file, "File cannot be null"); if (!file.exists()) { throw new InvalidPluginException(new FileNotFoundException(String.format("%s does not exist", file.getPath()))); } - try { - JarFile jar = new JarFile(file); - JarEntry entry = jar.getJarEntry("plugin.yml"); - if (entry == null) { - throw new InvalidPluginException(new FileNotFoundException("Jar does not contain plugin.yml")); - } - - InputStream stream = jar.getInputStream(entry); - - description = new PluginDescriptionFile(stream); - - stream.close(); - jar.close(); - } catch (IOException ex) { - throw new InvalidPluginException(ex); - } catch (YAMLException ex) { - throw new InvalidPluginException(ex); - } + PluginDescriptionFile description = getPluginDescription(file); File dataFolder = new File(file.getParentFile(), description.getName()); File oldDataFolder = getDataFolder(file); @@ -134,31 +113,8 @@ public class JavaPluginLoader implements PluginLoader { } } - if (!ignoreSoftDependencies) { - ArrayList softDepend; - - try { - softDepend = (ArrayList) description.getSoftDepend(); - if (softDepend == null) { - softDepend = new ArrayList(); - } - } catch (ClassCastException ex) { - throw new InvalidPluginException(ex); - } - - for (String pluginName : softDepend) { - if (loaders == null) { - throw new UnknownSoftDependencyException(pluginName); - } - PluginClassLoader current = loaders.get(pluginName); - - if (current == null) { - throw new UnknownSoftDependencyException(pluginName); - } - } - } - PluginClassLoader loader = null; + JavaPlugin result = null; try { URL[] urls = new URL[1]; @@ -189,6 +145,10 @@ public class JavaPluginLoader implements PluginLoader { return result; } + public Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException { + return loadPlugin(file); + } + protected File getDataFolder(File file) { File dataFolder = null; @@ -209,6 +169,44 @@ public class JavaPluginLoader implements PluginLoader { return dataFolder; } + public PluginDescriptionFile getPluginDescription(File file) throws InvalidDescriptionException, InvalidPluginException { + Validate.notNull(file, "File cannot be null"); + + JarFile jar = null; + InputStream stream = null; + + try { + jar = new JarFile(file); + JarEntry entry = jar.getJarEntry("plugin.yml"); + + if (entry == null) { + throw new InvalidPluginException(new FileNotFoundException("Jar does not contain plugin.yml")); + } + + stream = jar.getInputStream(entry); + + return new PluginDescriptionFile(stream); + + } catch (IOException ex) { + throw new InvalidPluginException(ex); + } catch (YAMLException ex) { + throw new InvalidDescriptionException(ex); + } finally { + if (jar != null) { + try { + jar.close(); + } catch (IOException e) { + } + } + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + public Pattern[] getPluginFileFilters() { return fileFilters; } -- cgit v1.2.3