summaryrefslogtreecommitdiffstats
path: root/pf4j
diff options
context:
space:
mode:
authorDecebal Suiu <decebal.suiu@gmail.com>2013-10-03 16:01:16 +0300
committerDecebal Suiu <decebal.suiu@gmail.com>2013-10-03 16:01:16 +0300
commit6df8db4c34abb0fbbeddda7cee39124a53c4cdf5 (patch)
tree955d991dc934ea369a1fa4fe939bf18da18e9831 /pf4j
parentaab4e0129a722f98a70ca1b9ed1917befd31c9f0 (diff)
downloadpf4j-6df8db4c34abb0fbbeddda7cee39124a53c4cdf5.tar.gz
pf4j-6df8db4c34abb0fbbeddda7cee39124a53c4cdf5.zip
add RuntimeMode with DEVELOPMENT and DEPLOYMENT values and working on DEVELOPMENT mode
Diffstat (limited to 'pf4j')
-rw-r--r--pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java852
-rw-r--r--pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java36
-rw-r--r--pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java13
-rw-r--r--pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java5
-rw-r--r--pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java9
-rw-r--r--pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java55
6 files changed, 573 insertions, 397 deletions
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
index 0c2628d..30658cd 100644
--- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
+++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
@@ -1,392 +1,460 @@
-/*
- * 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.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);
-
- /**
- * The plugins repository.
- */
- private File pluginsDirectory;
-
- private final ExtensionFinder extensionFinder;
-
- private final PluginDescriptorFinder pluginDescriptorFinder;
-
- private final 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;
-
- /**
- * Th plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins").
- */
- public DefaultPluginManager() {
- this(new File(System.getProperty("pf4j.pluginsDir", "plugins")));
- }
-
- /**
- * Constructs DefaultPluginManager which the given plugins directory.
- *
- * @param pluginsDirectory
- * the directory to search for plugins
- */
- public DefaultPluginManager(File pluginsDirectory) {
- this.pluginsDirectory = pluginsDirectory;
-
- 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());
- }
-
- @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;
- }
-
- /**
- * 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);
- }
- }
- }
-
- /**
- * Stop all active plugins.
- */
- @Override
- public void stopPlugins() {
- // stop started plugins in reverse order
- Collections.reverse(startedPlugins);
- for (PluginWrapper pluginWrapper : startedPlugins) {
- try {
- log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId());
- pluginWrapper.getPlugin().stop();
- pluginWrapper.setPluginState(PluginState.STOPPED);
- } catch (PluginException e) {
- log.error(e.getMessage(), e);
- }
- }
- }
-
- /**
- * 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);
- 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.length == 0) {
- log.info("No plugins");
- return;
- }
-
- // load any plugin from plugins directory
- for (File directory : directories) {
- try {
- loadPlugin(directory);
- } catch (PluginException e) {
- log.error(e.getMessage(), e);
- }
- }
-
- // resolve 'unresolvedPlugins'
- try {
- resolvePlugins();
- } catch (PluginException e) {
- log.error(e.getMessage(), e);
- }
- }
-
- /**
- * 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;
- }
-
- /**
- * 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;
- }
- }
-
- return null;
- }
-
- /**
- * Add the possibility to override the PluginDescriptorFinder.
- */
- protected PluginDescriptorFinder createPluginDescriptorFinder() {
- 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.
- */
- protected PluginClasspath createPluginClasspath() {
- 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();
- }
-
- private void loadPlugin(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;
- }
-
- // 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;
- }
-
- // 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());
- 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);
- }
-
- private void 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);
- // create directory for plugin
- pluginDirectory.mkdirs();
-
- // expand '.zip' file
- Unzip unzip = new Unzip();
- unzip.setSource(pluginArchiveFile);
- unzip.setDestination(pluginDirectory);
- unzip.extract();
- }
- }
-
- 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());
- }
- }
-
-}
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+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;
+ }
+
+ /**
+ * 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);
+ }
+ }
+ }
+
+ /**
+ * Stop all active plugins.
+ */
+ @Override
+ public void stopPlugins() {
+ // stop started plugins in reverse order
+ Collections.reverse(startedPlugins);
+ for (PluginWrapper pluginWrapper : startedPlugins) {
+ try {
+ log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId());
+ pluginWrapper.getPlugin().stop();
+ pluginWrapper.setPluginState(PluginState.STOPPED);
+ } catch (PluginException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * 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);
+ 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);
+ log.debug("Possible plugins: {}", Arrays.asList(directories));
+ if (directories.length == 0) {
+ log.info("No plugins");
+ return;
+ }
+
+ // load any plugin from plugins directory
+ for (File directory : directories) {
+ try {
+ loadPlugin(directory);
+ } catch (PluginException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ // resolve 'unresolvedPlugins'
+ try {
+ resolvePlugins();
+ } catch (PluginException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 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: '" + 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;
+ }
+ }
+
+ 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 void loadPlugin(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;
+ }
+
+ // 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;
+ }
+
+ // 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);
+ }
+
+ private void 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);
+ // create directory for plugin
+ pluginDirectory.mkdirs();
+
+ // expand '.zip' file
+ Unzip unzip = new Unzip();
+ unzip.setSource(pluginArchiveFile);
+ unzip.setDestination(pluginDirectory);
+ unzip.extract();
+ }
+ }
+
+ 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());
+ }
+ }
+
+}
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java
new file mode 100644
index 0000000..e14722c
--- /dev/null
+++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package ro.fortsoft.pf4j;
+
+/**
+ * Overwrite classes directories to "target/classes" and lib directories to "target/lib".
+ *
+ * @author Decebal Suiu
+ */
+public class DevelopmentPluginClasspath extends PluginClasspath {
+
+ private static final String DEVELOPMENT_CLASSES_DIRECTORY = "target/classes";
+ private static final String DEVELOPMENT_LIB_DIRECTORY = "target/lib";
+
+ public DevelopmentPluginClasspath() {
+ super();
+ }
+
+ @Override
+ protected void addResources() {
+ classesDirectories.add(DEVELOPMENT_CLASSES_DIRECTORY);
+ libDirectories.add(DEVELOPMENT_LIB_DIRECTORY);
+ }
+
+
+}
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java
index 23cd8f6..5fbe975 100644
--- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java
+++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java
@@ -28,16 +28,14 @@ public class PluginClasspath {
private static final String DEFAULT_CLASSES_DIRECTORY = "classes";
private static final String DEFAULT_LIB_DIRECTORY = "lib";
- private List<String> classesDirectories;
- private List<String> libDirectories;
+ protected List<String> classesDirectories;
+ protected List<String> libDirectories;
public PluginClasspath() {
classesDirectories = new ArrayList<String>();
libDirectories = new ArrayList<String>();
- // add defaults
- classesDirectories.add(DEFAULT_CLASSES_DIRECTORY);
- libDirectories.add(DEFAULT_LIB_DIRECTORY);
+ addResources();
}
public List<String> getClassesDirectories() {
@@ -56,4 +54,9 @@ public class PluginClasspath {
this.libDirectories = libDirectories;
}
+ protected void addResources() {
+ classesDirectories.add(DEFAULT_CLASSES_DIRECTORY);
+ libDirectories.add(DEFAULT_LIB_DIRECTORY);
+ }
+
}
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java
index 5e89b6b..7c78b89 100644
--- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java
+++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java
@@ -61,4 +61,9 @@ public interface PluginManager {
public <T> List<T> getExtensions(Class<T> type);
+ /**
+ * The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT.
+ */
+ public RuntimeMode getRuntimeMode();
+
}
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java
index 4dd18d1..d307cf0 100644
--- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java
+++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java
@@ -27,6 +27,7 @@ public class PluginWrapper {
PluginClassLoader pluginClassLoader;
Plugin plugin;
PluginState pluginState;
+ RuntimeMode runtimeMode;
public PluginWrapper(PluginDescriptor descriptor, String pluginPath, PluginClassLoader pluginClassLoader) {
this.descriptor = descriptor;
@@ -74,6 +75,10 @@ public class PluginWrapper {
return pluginState;
}
+ public RuntimeMode getRuntimeMode() {
+ return runtimeMode;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
@@ -114,6 +119,10 @@ public class PluginWrapper {
void setPluginState(PluginState pluginState) {
this.pluginState = pluginState;
}
+
+ void setRuntimeMode(RuntimeMode runtimeMode) {
+ this.runtimeMode = runtimeMode;
+ }
private Plugin createPluginInstance() throws Exception {
String pluginClassName = descriptor.getPluginClass();
diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java b/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java
new file mode 100644
index 0000000..0e7cd3a
--- /dev/null
+++ b/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package ro.fortsoft.pf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * @author Decebal Suiu
+ */
+public enum RuntimeMode {
+
+ DEVELOPMENT("development"), // development
+ DEPLOYMENT("deployment"); // deployment
+
+ private final String name;
+
+ private static final Map<String, RuntimeMode> map = new HashMap<String, RuntimeMode>();
+
+ static {
+ for (RuntimeMode mode : RuntimeMode.values()) {
+ map.put(mode.name, mode);
+ }
+ }
+
+ private RuntimeMode(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public static RuntimeMode byName(String name) {
+ if (map.containsKey(name)) {
+ return map.get(name);
+ }
+
+ throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name +
+ "'. Must be 'development' or 'deployment'.");
+ }
+
+} \ No newline at end of file