]> source.dussan.org Git - pf4j.git/commitdiff
Implement loading a single plugin archive 6/head
authorJames Moger <james.moger@gitblit.com>
Fri, 4 Apr 2014 23:57:03 +0000 (19:57 -0400)
committerJames Moger <james.moger@gitblit.com>
Fri, 4 Apr 2014 23:57:03 +0000 (19:57 -0400)
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java
pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java

index 86f11fcb15a6bee8838b2f23b01d4e8c52fd7dbf..99f7ecacc86d9322d6083469b68a82b5e39fb00e 100644 (file)
@@ -1,11 +1,11 @@
 /*
  * Copyright 2013 Decebal Suiu
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
  * the License. You may obtain a copy of the License in the LICENSE file, or at:
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  * specific language governing permissions and limitations under the License.
@@ -29,23 +29,28 @@ import org.slf4j.LoggerFactory;
 /**
  * The default implementation for ExtensionFinder.
  * All extensions declared in a plugin are indexed in a file "META-INF/extensions.idx".
- * This class lookup extensions in all extensions index files "META-INF/extensions.idx". 
- * 
+ * This class lookup extensions in all extensions index files "META-INF/extensions.idx".
+ *
  * @author Decebal Suiu
  */
 public class DefaultExtensionFinder implements ExtensionFinder {
 
        private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFinder.class);
-       
+
        private ClassLoader classLoader;
        private ExtensionFactory extensionFactory;
        private volatile Set<String> entries;
-       
+
        public DefaultExtensionFinder(ClassLoader classLoader) {
                this.classLoader = classLoader;
                this.extensionFactory = createExtensionFactory();
        }
-       
+
+       @Override
+       public void reset() {
+               entries = null;
+       }
+
        @Override
        public <T> List<ExtensionWrapper<T>> find(Class<T> type) {
         log.debug("Checking extension point '{}'", type.getName());
@@ -60,7 +65,7 @@ public class DefaultExtensionFinder implements ExtensionFinder {
         if (entries == null) {
                entries = readIndexFiles();
         }
-                
+
         for (String entry : entries) {
                try {
                        Class<?> extensionType = classLoader.loadClass(entry);
@@ -76,10 +81,10 @@ public class DefaultExtensionFinder implements ExtensionFinder {
                        log.warn("'{}' is not an extension for extension point '{}'", extensionType.getName(), type.getName());
                }
                } catch (ClassNotFoundException e) {
-                       log.error(e.getMessage(), e);           
+                       log.error(e.getMessage(), e);
                        }
         }
-        
+
         if (entries.isEmpty()) {
                log.debug("No extensions found for extension point '{}'", type.getName());
         } else {
@@ -88,21 +93,21 @@ public class DefaultExtensionFinder implements ExtensionFinder {
 
         // sort by "ordinal" property
         Collections.sort(result);
-               
+
                return result;
        }
-       
+
        /**
      * Add the possibility to override the ExtensionFactory.
      * The default implementation uses Class.newInstance() method.
      */
        protected ExtensionFactory createExtensionFactory() {
                return new ExtensionFactory() {
-                       
+
                        @Override
                        public Object create(Class<?> extensionType) {
                                log.debug("Create instance for extension '{}'", extensionType.getName());
-                               
+
                                try {
                                        return extensionType.newInstance();
                                } catch (InstantiationException e) {
@@ -110,17 +115,17 @@ public class DefaultExtensionFinder implements ExtensionFinder {
                                } catch (IllegalAccessException e) {
                                        log.error(e.getMessage(), e);
                                }
-                               
+
                                return null;
                        }
-                       
+
                };
        }
-       
+
        private Set<String> readIndexFiles() {
                log.debug("Reading extensions index files");
                Set<String> entries = new HashSet<String>();
-               
+
                try {
                        Enumeration<URL> indexFiles = classLoader.getResources(ExtensionsIndexer.EXTENSIONS_RESOURCE);
                        while (indexFiles.hasMoreElements()) {
@@ -129,7 +134,7 @@ public class DefaultExtensionFinder implements ExtensionFinder {
                        }
                } catch (IOException e) {
                        log.error(e.getMessage(), e);
-               }                       
+               }
 
         if (entries.isEmpty()) {
                log.debug("No extensions found");
@@ -143,14 +148,14 @@ public class DefaultExtensionFinder implements ExtensionFinder {
     private boolean isExtensionPoint(Class type) {
         return ExtensionPoint.class.isAssignableFrom(type);
     }
-       
+
        /**
         * Creates an extension instance.
         */
        public static interface ExtensionFactory {
 
                public Object create(Class<?> extensionType);
-               
+
        }
 
 }
index 7254206b2bd59164a3b81805aba36a1f35d2884c..557bb7764f122051b5f8aebf443e3d1b2f045ed4 100644 (file)
-/*\r
- * Copyright 2012 Decebal Suiu\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with\r
- * the License. You may obtain a copy of the License in the LICENSE file, or at:\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on\r
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\r
- * specific language governing permissions and limitations under the License.\r
- */\r
-package ro.fortsoft.pf4j;\r
-\r
-import java.io.File;\r
-import java.io.FileFilter;\r
-import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import ro.fortsoft.pf4j.util.AndFileFilter;\r
-import ro.fortsoft.pf4j.util.CompoundClassLoader;\r
-import ro.fortsoft.pf4j.util.DirectoryFileFilter;\r
-import ro.fortsoft.pf4j.util.FileUtils;\r
-import ro.fortsoft.pf4j.util.HiddenFilter;\r
-import ro.fortsoft.pf4j.util.NotFileFilter;\r
-import ro.fortsoft.pf4j.util.Unzip;\r
-import ro.fortsoft.pf4j.util.ZipFileFilter;\r
-\r
-/**\r
- * Default implementation of the PluginManager interface.\r
- *\r
- * @author Decebal Suiu\r
- */\r
-public class DefaultPluginManager implements PluginManager {\r
-\r
-       private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class);\r
-\r
-       public static final String DEFAULT_PLUGINS_DIRECTORY = "plugins";\r
-       public static final String DEVELOPMENT_PLUGINS_DIRECTORY = "../plugins";\r
-\r
-    /**\r
-     * The plugins repository.\r
-     */\r
-    private File pluginsDirectory;\r
-\r
-    private ExtensionFinder extensionFinder;\r
-\r
-    private PluginDescriptorFinder pluginDescriptorFinder;\r
-\r
-    private PluginClasspath pluginClasspath;\r
-\r
-    /**\r
-     * A map of plugins this manager is responsible for (the key is the 'pluginId').\r
-     */\r
-    private Map<String, PluginWrapper> plugins;\r
-\r
-    /**\r
-     * A map of plugin class loaders (he key is the 'pluginId').\r
-     */\r
-    private Map<String, PluginClassLoader> pluginClassLoaders;\r
-\r
-    /**\r
-     * A relation between 'pluginPath' and 'pluginId'\r
-     */\r
-    private Map<String, String> pathToIdMap;\r
-\r
-    /**\r
-     * A list with unresolved plugins (unresolved dependency).\r
-     */\r
-    private List<PluginWrapper> unresolvedPlugins;\r
-\r
-    /**\r
-     * A list with resolved plugins (resolved dependency).\r
-     */\r
-    private List<PluginWrapper> resolvedPlugins;\r
-\r
-    /**\r
-     * A list with started plugins.\r
-     */\r
-    private List<PluginWrapper> startedPlugins;\r
-\r
-    private List<String> enabledPlugins;\r
-    private List<String> disabledPlugins;\r
-\r
-    /**\r
-     * A compound class loader of resolved plugins.\r
-     */\r
-    protected CompoundClassLoader compoundClassLoader;\r
-\r
-    /**\r
-     * Cache value for the runtime mode. No need to re-read it because it wont change at\r
-        * runtime.\r
-     */\r
-    private RuntimeMode runtimeMode;\r
-\r
-    /**\r
-     * The plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins").\r
-     */\r
-    public DefaultPluginManager() {\r
-       this.pluginsDirectory = createPluginsDirectory();\r
-\r
-       initialize();\r
-    }\r
-\r
-    /**\r
-     * Constructs DefaultPluginManager which the given plugins directory.\r
-     *\r
-     * @param pluginsDirectory\r
-     *            the directory to search for plugins\r
-     */\r
-    public DefaultPluginManager(File pluginsDirectory) {\r
-        this.pluginsDirectory = pluginsDirectory;\r
-\r
-        initialize();\r
-    }\r
-\r
-       @Override\r
-    public List<PluginWrapper> getPlugins() {\r
-        return new ArrayList<PluginWrapper>(plugins.values());\r
-    }\r
-\r
-    @Override\r
-       public List<PluginWrapper> getResolvedPlugins() {\r
-               return resolvedPlugins;\r
-       }\r
-\r
-       public PluginWrapper getPlugin(String pluginId) {\r
-               return plugins.get(pluginId);\r
-       }\r
-\r
-       @Override\r
-    public List<PluginWrapper> getUnresolvedPlugins() {\r
-               return unresolvedPlugins;\r
-       }\r
-\r
-       @Override\r
-       public List<PluginWrapper> getStartedPlugins() {\r
-               return startedPlugins;\r
-       }\r
-\r
-    /**\r
-     * Start all active plugins.\r
-     */\r
-       @Override\r
-    public void startPlugins() {\r
-        for (PluginWrapper pluginWrapper : resolvedPlugins) {\r
-            try {\r
-               log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId());\r
-                               pluginWrapper.getPlugin().start();\r
-                               pluginWrapper.setPluginState(PluginState.STARTED);\r
-                               startedPlugins.add(pluginWrapper);\r
-                       } catch (PluginException e) {\r
-                               log.error(e.getMessage(), e);\r
-                       }\r
-        }\r
-    }\r
-\r
-       /**\r
-     * Start the specified plugin and it's dependencies.\r
-     */\r
-    @Override\r
-    public PluginState startPlugin(String pluginId) {\r
-       if (!plugins.containsKey(pluginId)) {\r
-               throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));\r
-       }\r
-       PluginWrapper pluginWrapper = plugins.get(pluginId);\r
-       PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();\r
-       if (pluginWrapper.getPluginState().equals(PluginState.STARTED)) {\r
-               log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());\r
-               return PluginState.STARTED;\r
-       }\r
-       for (PluginDependency dependency : pluginDescriptor.getDependencies()) {\r
-               startPlugin(dependency.getPluginId());\r
-       }\r
-       try {\r
-               log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());\r
-               pluginWrapper.getPlugin().start();\r
-               pluginWrapper.setPluginState(PluginState.STARTED);\r
-               startedPlugins.add(pluginWrapper);\r
-       } catch (PluginException e) {\r
-               log.error(e.getMessage(), e);\r
-       }\r
-       return pluginWrapper.getPluginState();\r
-    }\r
-\r
-    /**\r
-     * Stop all active plugins.\r
-     */\r
-    @Override\r
-    public void stopPlugins() {\r
-       // stop started plugins in reverse order\r
-       Collections.reverse(startedPlugins);\r
-       Iterator<PluginWrapper> itr = startedPlugins.iterator();\r
-        while (itr.hasNext()) {\r
-               PluginWrapper pluginWrapper = itr.next();\r
-               PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();\r
-            try {\r
-               log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());\r
-               pluginWrapper.getPlugin().stop();\r
-               pluginWrapper.setPluginState(PluginState.STOPPED);\r
-               itr.remove();\r
-                       } catch (PluginException e) {\r
-                               log.error(e.getMessage(), e);\r
-                       }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Stop the specified plugin and it's dependencies.\r
-     */\r
-    @Override\r
-    public PluginState stopPlugin(String pluginId) {\r
-       if (!plugins.containsKey(pluginId)) {\r
-               throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));\r
-       }\r
-       PluginWrapper pluginWrapper = plugins.get(pluginId);\r
-       PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();\r
-       if (pluginWrapper.getPluginState().equals(PluginState.STOPPED)) {\r
-               log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());\r
-               return PluginState.STOPPED;\r
-       }\r
-       for (PluginDependency dependency : pluginDescriptor.getDependencies()) {\r
-               stopPlugin(dependency.getPluginId());\r
-       }\r
-       try {\r
-               log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());\r
-               pluginWrapper.getPlugin().stop();\r
-               pluginWrapper.setPluginState(PluginState.STOPPED);\r
-               startedPlugins.remove(pluginWrapper);\r
-       } catch (PluginException e) {\r
-               log.error(e.getMessage(), e);\r
-       }\r
-       return pluginWrapper.getPluginState();\r
-    }\r
-\r
-    /**\r
-     * Load plugins.\r
-     */\r
-    @Override\r
-    public void loadPlugins() {\r
-       log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath());\r
-       // check for plugins directory\r
-        if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {\r
-            log.error("No '{}' directory", pluginsDirectory.getAbsolutePath());\r
-            return;\r
-        }\r
-\r
-        // expand all plugin archives\r
-        FileFilter zipFilter = new ZipFileFilter();\r
-        File[] zipFiles = pluginsDirectory.listFiles(zipFilter);\r
-        if (zipFiles != null) {\r
-               for (File zipFile : zipFiles) {\r
-                       try {\r
-                               expandPluginArchive(zipFile);\r
-                       } catch (IOException e) {\r
-                               log.error(e.getMessage(), e);\r
-                       }\r
-               }\r
-        }\r
-\r
-        // check for no plugins\r
-        List<FileFilter> filterList = new ArrayList<FileFilter>();\r
-        filterList.add(new DirectoryFileFilter());\r
-        filterList.add(new NotFileFilter(createHiddenPluginFilter()));\r
-        FileFilter pluginsFilter = new AndFileFilter(filterList);\r
-        File[] directories = pluginsDirectory.listFiles(pluginsFilter);\r
-        if (directories == null) {\r
-               directories = new File[0];\r
-        }\r
-        log.debug("Found possible {} plugins: {}", directories.length, directories);\r
-        if (directories.length == 0) {\r
-               log.info("No plugins");\r
-               return;\r
-        }\r
-\r
-        // load any plugin from plugins directory\r
-               for (File directory : directories) {\r
-                       try {\r
-                               loadPlugin(directory);\r
-                       } catch (PluginException e) {\r
-                               log.error(e.getMessage(), e);\r
-                       }\r
-               }\r
-\r
-        // resolve 'unresolvedPlugins'\r
-        try {\r
-                       resolvePlugins();\r
-               } catch (PluginException e) {\r
-                       log.error(e.getMessage(), e);\r
-               }\r
-    }\r
-\r
-    @Override\r
-    public boolean unloadPlugin(String pluginId) {\r
-       try {\r
-               PluginState state = stopPlugin(pluginId);\r
-               if (!PluginState.STOPPED.equals(state)) {\r
-                       return false;\r
-               }\r
-\r
-               PluginWrapper pluginWrapper = plugins.get(pluginId);\r
-               PluginDescriptor descriptor = pluginWrapper.getDescriptor();\r
-               List<PluginDependency> dependencies = descriptor.getDependencies();\r
-               for (PluginDependency dependency : dependencies) {\r
-                       if (!unloadPlugin(dependency.getPluginId())) {\r
-                               return false;\r
-                       }\r
-               }\r
-\r
-               // remove the plugin\r
-               plugins.remove(pluginId);\r
-               resolvedPlugins.remove(pluginWrapper);\r
-               pathToIdMap.remove(pluginWrapper.getPluginPath());\r
-\r
-               // remove the classloader\r
-               if (pluginClassLoaders.containsKey(pluginId)) {\r
-                       PluginClassLoader classLoader = pluginClassLoaders.remove(pluginId);\r
-                       compoundClassLoader.removeLoader(classLoader);\r
-                       try {\r
-                               classLoader.close();\r
-                       } catch (IOException e) {\r
-                               log.error(e.getMessage(), e);\r
-                       }\r
-               }\r
-               return true;\r
-       } catch (IllegalArgumentException e) {\r
-               // ignore not found exceptions because this method is recursive\r
-       }\r
-       return false;\r
-    }\r
-\r
-    @Override\r
-       public boolean deletePlugin(String pluginId) {\r
-       if (!plugins.containsKey(pluginId)) {\r
-               throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));\r
-       }\r
-               PluginWrapper pw = getPlugin(pluginId);\r
-               PluginState state = stopPlugin(pluginId);\r
-\r
-               if (PluginState.STOPPED != state) {\r
-                       log.error("Failed to stop plugin {} on delete", pluginId);\r
-                       return false;\r
-               }\r
-\r
-               if (!unloadPlugin(pluginId)) {\r
-                       log.error("Failed to unload plugin {} on delete", pluginId);\r
-                       return false;\r
-               }\r
-\r
-               File pluginFolder = new File(pluginsDirectory, pw.getPluginPath());\r
-               File pluginZip = null;\r
-\r
-               FileFilter zipFilter = new ZipFileFilter();\r
-        File[] zipFiles = pluginsDirectory.listFiles(zipFilter);\r
-        if (zipFiles != null) {\r
-               // strip prepended / from the plugin path\r
-               String dirName = pw.getPluginPath().substring(1);\r
-               // find the zip file that matches the plugin path\r
-               for (File zipFile : zipFiles) {\r
-                       String name = zipFile.getName().substring(0, zipFile.getName().lastIndexOf('.'));\r
-                       if (name.equals(dirName)) {\r
-                               pluginZip = zipFile;\r
-                               break;\r
-                       }\r
-               }\r
-        }\r
-\r
-               if (pluginFolder.exists()) {\r
-                       FileUtils.delete(pluginFolder);\r
-               }\r
-               if (pluginZip != null && pluginZip.exists()) {\r
-                       FileUtils.delete(pluginZip);\r
-               }\r
-               return true;\r
-       }\r
-\r
-    /**\r
-     * Get plugin class loader for this path.\r
-     */\r
-    @Override\r
-    public PluginClassLoader getPluginClassLoader(String pluginId) {\r
-       return pluginClassLoaders.get(pluginId);\r
-    }\r
-\r
-    @Override\r
-       public <T> List<T> getExtensions(Class<T> type) {\r
-               List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type);\r
-               List<T> extensions = new ArrayList<T>(extensionsWrapper.size());\r
-               for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) {\r
-                       extensions.add(extensionWrapper.getInstance());\r
-               }\r
-\r
-               return extensions;\r
-       }\r
-\r
-    @Override\r
-       public RuntimeMode getRuntimeMode() {\r
-       if (runtimeMode == null) {\r
-               // retrieves the runtime mode from system\r
-               String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString());\r
-               runtimeMode = RuntimeMode.byName(modeAsString);\r
-\r
-               log.info("PF4J runtime mode is '{}'", runtimeMode);\r
-\r
-       }\r
-\r
-               return runtimeMode;\r
-       }\r
-\r
-    /**\r
-     * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'.\r
-     */\r
-    public PluginWrapper whichPlugin(Class<?> clazz) {\r
-        ClassLoader classLoader = clazz.getClassLoader();\r
-        for (PluginWrapper plugin : resolvedPlugins) {\r
-            if (plugin.getPluginClassLoader() == classLoader) {\r
-               return plugin;\r
-            }\r
-        }\r
-\r
-        return null;\r
-    }\r
-\r
-       /**\r
-        * Add the possibility to override the PluginDescriptorFinder.\r
-        * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a\r
-        * PropertiesPluginDescriptorFinder is returned else this method returns\r
-        * DefaultPluginDescriptorFinder.\r
-        */\r
-    protected PluginDescriptorFinder createPluginDescriptorFinder() {\r
-       if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) {\r
-               return new PropertiesPluginDescriptorFinder();\r
-       }\r
-\r
-       return new DefaultPluginDescriptorFinder(pluginClasspath);\r
-    }\r
-\r
-    /**\r
-     * Add the possibility to override the ExtensionFinder.\r
-     */\r
-    protected ExtensionFinder createExtensionFinder() {\r
-       return new DefaultExtensionFinder(compoundClassLoader);\r
-    }\r
-\r
-    /**\r
-     * Add the possibility to override the PluginClassPath.\r
-     * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a\r
-        * DevelopmentPluginClasspath is returned else this method returns\r
-        * PluginClasspath.\r
-     */\r
-    protected PluginClasspath createPluginClasspath() {\r
-       if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) {\r
-               return new DevelopmentPluginClasspath();\r
-       }\r
-\r
-       return new PluginClasspath();\r
-    }\r
-\r
-    protected boolean isPluginDisabled(String pluginId) {\r
-       if (enabledPlugins.isEmpty()) {\r
-               return disabledPlugins.contains(pluginId);\r
-       }\r
-\r
-       return !enabledPlugins.contains(pluginId);\r
-    }\r
-\r
-    protected FileFilter createHiddenPluginFilter() {\r
-       return new HiddenFilter();\r
-    }\r
-\r
-    /**\r
-     * Add the possibility to override the plugins directory.\r
-     * If a "pf4j.pluginsDir" system property is defined than this method returns\r
-     * that directory.\r
-     * If getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a\r
-        * DEVELOPMENT_PLUGINS_DIRECTORY ("../plugins") is returned else this method returns\r
-        * DEFAULT_PLUGINS_DIRECTORY ("plugins").\r
-     * @return\r
-     */\r
-    protected File createPluginsDirectory() {\r
-       String pluginsDir = System.getProperty("pf4j.pluginsDir");\r
-       if (pluginsDir == null) {\r
-               if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) {\r
-                       pluginsDir = DEVELOPMENT_PLUGINS_DIRECTORY;\r
-               } else {\r
-                       pluginsDir = DEFAULT_PLUGINS_DIRECTORY;\r
-               }\r
-       }\r
-\r
-       return new File(pluginsDir);\r
-    }\r
-\r
-       private void initialize() {\r
-               plugins = new HashMap<String, PluginWrapper>();\r
-        pluginClassLoaders = new HashMap<String, PluginClassLoader>();\r
-        pathToIdMap = new HashMap<String, String>();\r
-        unresolvedPlugins = new ArrayList<PluginWrapper>();\r
-        resolvedPlugins = new ArrayList<PluginWrapper>();\r
-        startedPlugins = new ArrayList<PluginWrapper>();\r
-        disabledPlugins = new ArrayList<String>();\r
-        compoundClassLoader = new CompoundClassLoader();\r
-\r
-        pluginClasspath = createPluginClasspath();\r
-        pluginDescriptorFinder = createPluginDescriptorFinder();\r
-        extensionFinder = createExtensionFinder();\r
-\r
-        try {\r
-               // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file)\r
-               enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true);\r
-               log.info("Enabled plugins: {}", enabledPlugins);\r
-\r
-               // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file)\r
-               disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true);\r
-               log.info("Disabled plugins: {}", disabledPlugins);\r
-        } catch (IOException e) {\r
-               log.error(e.getMessage(), e);\r
-        }\r
-\r
-        System.setProperty("pf4j.pluginsDir", pluginsDirectory.getAbsolutePath());\r
-       }\r
-\r
-       private void loadPlugin(File pluginDirectory) throws PluginException {\r
-        // try to load the plugin\r
-               String pluginName = pluginDirectory.getName();\r
-        String pluginPath = "/".concat(pluginName);\r
-\r
-        // test for plugin duplication\r
-        if (plugins.get(pathToIdMap.get(pluginPath)) != null) {\r
-            return;\r
-        }\r
-\r
-        // retrieves the plugin descriptor\r
-        log.debug("Find plugin descriptor '{}'", pluginPath);\r
-        PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory);\r
-        log.debug("Descriptor " + pluginDescriptor);\r
-        String pluginClassName = pluginDescriptor.getPluginClass();\r
-        log.debug("Class '{}' for plugin '{}'",  pluginClassName, pluginPath);\r
-\r
-        // test for disabled plugin\r
-        if (isPluginDisabled(pluginDescriptor.getPluginId())) {\r
-               log.info("Plugin '{}' is disabled", pluginPath);\r
-            return;\r
-        }\r
-\r
-        // load plugin\r
-        log.debug("Loading plugin '{}'", pluginPath);\r
-        PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath);\r
-        pluginLoader.load();\r
-        log.debug("Loaded plugin '{}'", pluginPath);\r
-\r
-        // create the plugin wrapper\r
-        log.debug("Creating wrapper for plugin '{}'", pluginPath);\r
-        PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader());\r
-        pluginWrapper.setRuntimeMode(getRuntimeMode());\r
-        log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath);\r
-\r
-        String pluginId = pluginDescriptor.getPluginId();\r
-\r
-        // add plugin to the list with plugins\r
-        plugins.put(pluginId, pluginWrapper);\r
-        unresolvedPlugins.add(pluginWrapper);\r
-\r
-        // add plugin class loader to the list with class loaders\r
-        PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader();\r
-        pluginClassLoaders.put(pluginId, pluginClassLoader);\r
-    }\r
-\r
-    private void expandPluginArchive(File pluginArchiveFile) throws IOException {\r
-       String fileName = pluginArchiveFile.getName();\r
-        long pluginArchiveDate = pluginArchiveFile.lastModified();\r
-        String pluginName = fileName.substring(0, fileName.length() - 4);\r
-        File pluginDirectory = new File(pluginsDirectory, pluginName);\r
-        // check if exists directory or the '.zip' file is "newer" than directory\r
-        if (!pluginDirectory.exists() || (pluginArchiveDate > pluginDirectory.lastModified())) {\r
-               log.debug("Expand plugin archive '{}' in '{}'", pluginArchiveFile, pluginDirectory);\r
-            // create directory for plugin\r
-            pluginDirectory.mkdirs();\r
-\r
-            // expand '.zip' file\r
-            Unzip unzip = new Unzip();\r
-            unzip.setSource(pluginArchiveFile);\r
-            unzip.setDestination(pluginDirectory);\r
-            unzip.extract();\r
-        }\r
-    }\r
-\r
-       private void resolvePlugins() throws PluginException {\r
-               resolveDependencies();\r
-       }\r
-\r
-       private void resolveDependencies() throws PluginException {\r
-               DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins);\r
-               resolvedPlugins = dependencyResolver.getSortedPlugins();\r
-        for (PluginWrapper pluginWrapper : resolvedPlugins) {\r
-               unresolvedPlugins.remove(pluginWrapper);\r
-               compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader());\r
-               log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId());\r
-        }\r
-       }\r
-\r
-}\r
+/*
+ * Copyright 2012 Decebal Suiu
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
+ * the License. You may obtain a copy of the License in the LICENSE file, or at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package ro.fortsoft.pf4j;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ro.fortsoft.pf4j.util.AndFileFilter;
+import ro.fortsoft.pf4j.util.CompoundClassLoader;
+import ro.fortsoft.pf4j.util.DirectoryFileFilter;
+import ro.fortsoft.pf4j.util.FileUtils;
+import ro.fortsoft.pf4j.util.HiddenFilter;
+import ro.fortsoft.pf4j.util.NotFileFilter;
+import ro.fortsoft.pf4j.util.Unzip;
+import ro.fortsoft.pf4j.util.ZipFileFilter;
+
+/**
+ * Default implementation of the PluginManager interface.
+ *
+ * @author Decebal Suiu
+ */
+public class DefaultPluginManager implements PluginManager {
+
+       private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class);
+
+       public static final String DEFAULT_PLUGINS_DIRECTORY = "plugins";
+       public static final String DEVELOPMENT_PLUGINS_DIRECTORY = "../plugins";
+
+    /**
+     * The plugins repository.
+     */
+    private File pluginsDirectory;
+
+    private ExtensionFinder extensionFinder;
+
+    private PluginDescriptorFinder pluginDescriptorFinder;
+
+    private PluginClasspath pluginClasspath;
+
+    /**
+     * A map of plugins this manager is responsible for (the key is the 'pluginId').
+     */
+    private Map<String, PluginWrapper> plugins;
+
+    /**
+     * A map of plugin class loaders (he key is the 'pluginId').
+     */
+    private Map<String, PluginClassLoader> pluginClassLoaders;
+
+    /**
+     * A relation between 'pluginPath' and 'pluginId'
+     */
+    private Map<String, String> pathToIdMap;
+
+    /**
+     * A list with unresolved plugins (unresolved dependency).
+     */
+    private List<PluginWrapper> unresolvedPlugins;
+
+    /**
+     * A list with resolved plugins (resolved dependency).
+     */
+    private List<PluginWrapper> resolvedPlugins;
+
+    /**
+     * A list with started plugins.
+     */
+    private List<PluginWrapper> startedPlugins;
+
+    private List<String> enabledPlugins;
+    private List<String> disabledPlugins;
+
+    /**
+     * A compound class loader of resolved plugins.
+     */
+    protected CompoundClassLoader compoundClassLoader;
+
+    /**
+     * Cache value for the runtime mode. No need to re-read it because it wont change at
+        * runtime.
+     */
+    private RuntimeMode runtimeMode;
+
+    /**
+     * The plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins").
+     */
+    public DefaultPluginManager() {
+       this.pluginsDirectory = createPluginsDirectory();
+
+       initialize();
+    }
+
+    /**
+     * Constructs DefaultPluginManager which the given plugins directory.
+     *
+     * @param pluginsDirectory
+     *            the directory to search for plugins
+     */
+    public DefaultPluginManager(File pluginsDirectory) {
+        this.pluginsDirectory = pluginsDirectory;
+
+        initialize();
+    }
+
+       @Override
+    public List<PluginWrapper> getPlugins() {
+        return new ArrayList<PluginWrapper>(plugins.values());
+    }
+
+    @Override
+       public List<PluginWrapper> getResolvedPlugins() {
+               return resolvedPlugins;
+       }
+
+       public PluginWrapper getPlugin(String pluginId) {
+               return plugins.get(pluginId);
+       }
+
+       @Override
+    public List<PluginWrapper> getUnresolvedPlugins() {
+               return unresolvedPlugins;
+       }
+
+       @Override
+       public List<PluginWrapper> getStartedPlugins() {
+               return startedPlugins;
+       }
+
+       @Override
+       public String loadPlugin(File pluginArchiveFile) {
+               if (pluginArchiveFile == null || !pluginArchiveFile.exists()) {
+                       throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginArchiveFile));
+               }
+
+               File pluginDirectory = null;
+               try {
+                       pluginDirectory = expandPluginArchive(pluginArchiveFile);
+               } catch (IOException e) {
+                       log.error(e.getMessage(), e);
+               }
+               if (pluginDirectory == null || !pluginDirectory.exists()) {
+                       throw new IllegalArgumentException(String.format("Failed to expand %s", pluginArchiveFile));
+               }
+
+               try {
+                       PluginWrapper pluginWrapper = loadPluginDirectory(pluginDirectory);
+                       // TODO uninstalled plugin dependencies?
+               unresolvedPlugins.remove(pluginWrapper);
+               resolvedPlugins.add(pluginWrapper);
+               compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader());
+               extensionFinder.reset();
+                       return pluginWrapper.getDescriptor().getPluginId();
+               } catch (PluginException e) {
+                       log.error(e.getMessage(), e);
+               }
+               return null;
+       }
+
+    /**
+     * Start all active plugins.
+     */
+       @Override
+    public void startPlugins() {
+        for (PluginWrapper pluginWrapper : resolvedPlugins) {
+            try {
+               log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId());
+                               pluginWrapper.getPlugin().start();
+                               pluginWrapper.setPluginState(PluginState.STARTED);
+                               startedPlugins.add(pluginWrapper);
+                       } catch (PluginException e) {
+                               log.error(e.getMessage(), e);
+                       }
+        }
+    }
+
+       /**
+     * Start the specified plugin and it's dependencies.
+     */
+    @Override
+    public PluginState startPlugin(String pluginId) {
+       if (!plugins.containsKey(pluginId)) {
+               throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
+       }
+       PluginWrapper pluginWrapper = plugins.get(pluginId);
+       PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
+       if (pluginWrapper.getPluginState().equals(PluginState.STARTED)) {
+               log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
+               return PluginState.STARTED;
+       }
+       for (PluginDependency dependency : pluginDescriptor.getDependencies()) {
+               startPlugin(dependency.getPluginId());
+       }
+       try {
+               log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
+               pluginWrapper.getPlugin().start();
+               pluginWrapper.setPluginState(PluginState.STARTED);
+               startedPlugins.add(pluginWrapper);
+       } catch (PluginException e) {
+               log.error(e.getMessage(), e);
+       }
+       return pluginWrapper.getPluginState();
+    }
+
+    /**
+     * Stop all active plugins.
+     */
+    @Override
+    public void stopPlugins() {
+       // stop started plugins in reverse order
+       Collections.reverse(startedPlugins);
+       Iterator<PluginWrapper> itr = startedPlugins.iterator();
+        while (itr.hasNext()) {
+               PluginWrapper pluginWrapper = itr.next();
+               PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
+            try {
+               log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
+               pluginWrapper.getPlugin().stop();
+               pluginWrapper.setPluginState(PluginState.STOPPED);
+               itr.remove();
+                       } catch (PluginException e) {
+                               log.error(e.getMessage(), e);
+                       }
+        }
+    }
+
+    /**
+     * Stop the specified plugin and it's dependencies.
+     */
+    @Override
+    public PluginState stopPlugin(String pluginId) {
+       if (!plugins.containsKey(pluginId)) {
+               throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
+       }
+       PluginWrapper pluginWrapper = plugins.get(pluginId);
+       PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
+       if (pluginWrapper.getPluginState().equals(PluginState.STOPPED)) {
+               log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
+               return PluginState.STOPPED;
+       }
+       for (PluginDependency dependency : pluginDescriptor.getDependencies()) {
+               stopPlugin(dependency.getPluginId());
+       }
+       try {
+               log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
+               pluginWrapper.getPlugin().stop();
+               pluginWrapper.setPluginState(PluginState.STOPPED);
+               startedPlugins.remove(pluginWrapper);
+       } catch (PluginException e) {
+               log.error(e.getMessage(), e);
+       }
+       return pluginWrapper.getPluginState();
+    }
+
+    /**
+     * Load plugins.
+     */
+    @Override
+    public void loadPlugins() {
+       log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath());
+       // check for plugins directory
+        if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {
+            log.error("No '{}' directory", pluginsDirectory.getAbsolutePath());
+            return;
+        }
+
+        // expand all plugin archives
+        FileFilter zipFilter = new ZipFileFilter();
+        File[] zipFiles = pluginsDirectory.listFiles(zipFilter);
+        if (zipFiles != null) {
+               for (File zipFile : zipFiles) {
+                       try {
+                               expandPluginArchive(zipFile);
+                       } catch (IOException e) {
+                               log.error(e.getMessage(), e);
+                       }
+               }
+        }
+
+        // check for no plugins
+        List<FileFilter> filterList = new ArrayList<FileFilter>();
+        filterList.add(new DirectoryFileFilter());
+        filterList.add(new NotFileFilter(createHiddenPluginFilter()));
+        FileFilter pluginsFilter = new AndFileFilter(filterList);
+        File[] directories = pluginsDirectory.listFiles(pluginsFilter);
+        if (directories == null) {
+               directories = new File[0];
+        }
+        log.debug("Found possible {} plugins: {}", directories.length, directories);
+        if (directories.length == 0) {
+               log.info("No plugins");
+               return;
+        }
+
+        // load any plugin from plugins directory
+               for (File directory : directories) {
+                       try {
+                               loadPluginDirectory(directory);
+               } catch (PluginException e) {
+                       log.error(e.getMessage(), e);
+               }
+               }
+
+        // resolve 'unresolvedPlugins'
+        try {
+                       resolvePlugins();
+               } catch (PluginException e) {
+                       log.error(e.getMessage(), e);
+               }
+    }
+
+    @Override
+    public boolean unloadPlugin(String pluginId) {
+       try {
+               PluginState state = stopPlugin(pluginId);
+               if (!PluginState.STOPPED.equals(state)) {
+                       return false;
+               }
+
+               PluginWrapper pluginWrapper = plugins.get(pluginId);
+               PluginDescriptor descriptor = pluginWrapper.getDescriptor();
+               List<PluginDependency> dependencies = descriptor.getDependencies();
+               for (PluginDependency dependency : dependencies) {
+                       if (!unloadPlugin(dependency.getPluginId())) {
+                               return false;
+                       }
+               }
+
+               // remove the plugin
+               plugins.remove(pluginId);
+               resolvedPlugins.remove(pluginWrapper);
+               pathToIdMap.remove(pluginWrapper.getPluginPath());
+               extensionFinder.reset();
+
+               // remove the classloader
+               if (pluginClassLoaders.containsKey(pluginId)) {
+                       PluginClassLoader classLoader = pluginClassLoaders.remove(pluginId);
+                       compoundClassLoader.removeLoader(classLoader);
+                       try {
+                               classLoader.close();
+                       } catch (IOException e) {
+                               log.error(e.getMessage(), e);
+                       }
+               }
+               return true;
+       } catch (IllegalArgumentException e) {
+               // ignore not found exceptions because this method is recursive
+       }
+       return false;
+    }
+
+    @Override
+       public boolean deletePlugin(String pluginId) {
+       if (!plugins.containsKey(pluginId)) {
+               throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
+       }
+               PluginWrapper pw = getPlugin(pluginId);
+               PluginState state = stopPlugin(pluginId);
+
+               if (PluginState.STOPPED != state) {
+                       log.error("Failed to stop plugin {} on delete", pluginId);
+                       return false;
+               }
+
+               if (!unloadPlugin(pluginId)) {
+                       log.error("Failed to unload plugin {} on delete", pluginId);
+                       return false;
+               }
+
+               File pluginFolder = new File(pluginsDirectory, pw.getPluginPath());
+               File pluginZip = null;
+
+               FileFilter zipFilter = new ZipFileFilter();
+        File[] zipFiles = pluginsDirectory.listFiles(zipFilter);
+        if (zipFiles != null) {
+               // strip prepended / from the plugin path
+               String dirName = pw.getPluginPath().substring(1);
+               // find the zip file that matches the plugin path
+               for (File zipFile : zipFiles) {
+                       String name = zipFile.getName().substring(0, zipFile.getName().lastIndexOf('.'));
+                       if (name.equals(dirName)) {
+                               pluginZip = zipFile;
+                               break;
+                       }
+               }
+        }
+
+               if (pluginFolder.exists()) {
+                       FileUtils.delete(pluginFolder);
+               }
+               if (pluginZip != null && pluginZip.exists()) {
+                       FileUtils.delete(pluginZip);
+               }
+               return true;
+       }
+
+    /**
+     * Get plugin class loader for this path.
+     */
+    @Override
+    public PluginClassLoader getPluginClassLoader(String pluginId) {
+       return pluginClassLoaders.get(pluginId);
+    }
+
+    @Override
+       public <T> List<T> getExtensions(Class<T> type) {
+               List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type);
+               List<T> extensions = new ArrayList<T>(extensionsWrapper.size());
+               for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) {
+                       extensions.add(extensionWrapper.getInstance());
+               }
+
+               return extensions;
+       }
+
+    @Override
+       public RuntimeMode getRuntimeMode() {
+       if (runtimeMode == null) {
+               // retrieves the runtime mode from system
+               String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString());
+               runtimeMode = RuntimeMode.byName(modeAsString);
+
+               log.info("PF4J runtime mode is '{}'", runtimeMode);
+
+       }
+
+               return runtimeMode;
+       }
+
+    /**
+     * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'.
+     */
+    public PluginWrapper whichPlugin(Class<?> clazz) {
+        ClassLoader classLoader = clazz.getClassLoader();
+        for (PluginWrapper plugin : resolvedPlugins) {
+            if (plugin.getPluginClassLoader() == classLoader) {
+               return plugin;
+            }
+        }
+        log.warn("Failed to find the plugin for {}", clazz);
+        return null;
+    }
+
+       /**
+        * Add the possibility to override the PluginDescriptorFinder.
+        * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a
+        * PropertiesPluginDescriptorFinder is returned else this method returns
+        * DefaultPluginDescriptorFinder.
+        */
+    protected PluginDescriptorFinder createPluginDescriptorFinder() {
+       if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) {
+               return new PropertiesPluginDescriptorFinder();
+       }
+
+       return new DefaultPluginDescriptorFinder(pluginClasspath);
+    }
+
+    /**
+     * Add the possibility to override the ExtensionFinder.
+     */
+    protected ExtensionFinder createExtensionFinder() {
+       return new DefaultExtensionFinder(compoundClassLoader);
+    }
+
+    /**
+     * Add the possibility to override the PluginClassPath.
+     * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a
+        * DevelopmentPluginClasspath is returned else this method returns
+        * PluginClasspath.
+     */
+    protected PluginClasspath createPluginClasspath() {
+       if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) {
+               return new DevelopmentPluginClasspath();
+       }
+
+       return new PluginClasspath();
+    }
+
+    protected boolean isPluginDisabled(String pluginId) {
+       if (enabledPlugins.isEmpty()) {
+               return disabledPlugins.contains(pluginId);
+       }
+
+       return !enabledPlugins.contains(pluginId);
+    }
+
+    protected FileFilter createHiddenPluginFilter() {
+       return new HiddenFilter();
+    }
+
+    /**
+     * Add the possibility to override the plugins directory.
+     * If a "pf4j.pluginsDir" system property is defined than this method returns
+     * that directory.
+     * If getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a
+        * DEVELOPMENT_PLUGINS_DIRECTORY ("../plugins") is returned else this method returns
+        * DEFAULT_PLUGINS_DIRECTORY ("plugins").
+     * @return
+     */
+    protected File createPluginsDirectory() {
+       String pluginsDir = System.getProperty("pf4j.pluginsDir");
+       if (pluginsDir == null) {
+               if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) {
+                       pluginsDir = DEVELOPMENT_PLUGINS_DIRECTORY;
+               } else {
+                       pluginsDir = DEFAULT_PLUGINS_DIRECTORY;
+               }
+       }
+
+       return new File(pluginsDir);
+    }
+
+       private void initialize() {
+               plugins = new HashMap<String, PluginWrapper>();
+        pluginClassLoaders = new HashMap<String, PluginClassLoader>();
+        pathToIdMap = new HashMap<String, String>();
+        unresolvedPlugins = new ArrayList<PluginWrapper>();
+        resolvedPlugins = new ArrayList<PluginWrapper>();
+        startedPlugins = new ArrayList<PluginWrapper>();
+        disabledPlugins = new ArrayList<String>();
+        compoundClassLoader = new CompoundClassLoader();
+
+        pluginClasspath = createPluginClasspath();
+        pluginDescriptorFinder = createPluginDescriptorFinder();
+        extensionFinder = createExtensionFinder();
+
+        try {
+               // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file)
+               enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true);
+               log.info("Enabled plugins: {}", enabledPlugins);
+
+               // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file)
+               disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true);
+               log.info("Disabled plugins: {}", disabledPlugins);
+        } catch (IOException e) {
+               log.error(e.getMessage(), e);
+        }
+
+        System.setProperty("pf4j.pluginsDir", pluginsDirectory.getAbsolutePath());
+       }
+
+       private PluginWrapper loadPluginDirectory(File pluginDirectory) throws PluginException {
+        // try to load the plugin
+               String pluginName = pluginDirectory.getName();
+        String pluginPath = "/".concat(pluginName);
+
+        // test for plugin duplication
+        if (plugins.get(pathToIdMap.get(pluginPath)) != null) {
+            return null;
+        }
+
+        // retrieves the plugin descriptor
+        log.debug("Find plugin descriptor '{}'", pluginPath);
+        PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory);
+        log.debug("Descriptor " + pluginDescriptor);
+        String pluginClassName = pluginDescriptor.getPluginClass();
+        log.debug("Class '{}' for plugin '{}'",  pluginClassName, pluginPath);
+
+        // test for disabled plugin
+        if (isPluginDisabled(pluginDescriptor.getPluginId())) {
+               log.info("Plugin '{}' is disabled", pluginPath);
+            return null;
+        }
+
+        // load plugin
+        log.debug("Loading plugin '{}'", pluginPath);
+        PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath);
+        pluginLoader.load();
+        log.debug("Loaded plugin '{}'", pluginPath);
+
+        // create the plugin wrapper
+        log.debug("Creating wrapper for plugin '{}'", pluginPath);
+        PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader());
+        pluginWrapper.setRuntimeMode(getRuntimeMode());
+        log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath);
+
+        String pluginId = pluginDescriptor.getPluginId();
+
+        // add plugin to the list with plugins
+        plugins.put(pluginId, pluginWrapper);
+        unresolvedPlugins.add(pluginWrapper);
+
+        // add plugin class loader to the list with class loaders
+        PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader();
+        pluginClassLoaders.put(pluginId, pluginClassLoader);
+
+        return pluginWrapper;
+    }
+
+    private File expandPluginArchive(File pluginArchiveFile) throws IOException {
+       String fileName = pluginArchiveFile.getName();
+        long pluginArchiveDate = pluginArchiveFile.lastModified();
+        String pluginName = fileName.substring(0, fileName.length() - 4);
+        File pluginDirectory = new File(pluginsDirectory, pluginName);
+        // check if exists directory or the '.zip' file is "newer" than directory
+        if (!pluginDirectory.exists() || (pluginArchiveDate > pluginDirectory.lastModified())) {
+               log.debug("Expand plugin archive '{}' in '{}'", pluginArchiveFile, pluginDirectory);
+
+               // do not overwrite an old version, remove it
+               if (pluginDirectory.exists()) {
+                       FileUtils.delete(pluginDirectory);
+               }
+
+            // create directory for plugin
+            pluginDirectory.mkdirs();
+
+            // expand '.zip' file
+            Unzip unzip = new Unzip();
+            unzip.setSource(pluginArchiveFile);
+            unzip.setDestination(pluginDirectory);
+            unzip.extract();
+        }
+        return pluginDirectory;
+    }
+
+       private void resolvePlugins() throws PluginException {
+               resolveDependencies();
+       }
+
+       private void resolveDependencies() throws PluginException {
+               DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins);
+               resolvedPlugins = dependencyResolver.getSortedPlugins();
+        for (PluginWrapper pluginWrapper : resolvedPlugins) {
+               unresolvedPlugins.remove(pluginWrapper);
+               compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader());
+               log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId());
+        }
+       }
+
+}
index 2eeca18920e8812815a2dc843992a4c128d5ff53..7e22a4a9f86aadedfbe953e0110b1736634ffb86 100644 (file)
@@ -1,11 +1,11 @@
 /*
  * Copyright 2012 Decebal Suiu
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
  * the License. You may obtain a copy of the License in the LICENSE file, or at:
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  * specific language governing permissions and limitations under the License.
@@ -20,5 +20,7 @@ import java.util.List;
 public interface ExtensionFinder {
 
        public <T> List<ExtensionWrapper<T>> find(Class<T> type);
-       
+
+       public void reset();
+
 }
index 57fb890f2e8dbe8809ad163bfd8cf01679a77995..ec95c4e513323b4848292616771dcca02099a6d5 100644 (file)
@@ -12,6 +12,7 @@
  */
 package ro.fortsoft.pf4j;
 
+import java.io.File;
 import java.util.List;
 
 /**
@@ -47,6 +48,14 @@ public interface PluginManager {
      */
     public void loadPlugins();
 
+    /**
+     * Load a plugin.
+     *
+     * @param pluginArchiveFile
+     * @return the pluginId of the installed plugin or null
+     */
+       public String loadPlugin(File pluginArchiveFile);
+
     /**
      * Start all active plugins.
      */