diff --git a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java index fa779e2..c9738eb 100644 --- a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java +++ b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java @@ -21,6 +21,7 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.JarPluginManager; import ro.fortsoft.pf4j.PluginManager; import ro.fortsoft.pf4j.PluginWrapper; import ro.fortsoft.pf4j.demo.api.Greeting; @@ -38,6 +39,7 @@ public class Boot { // create the plugin manager final PluginManager pluginManager = new DefaultPluginManager(); +// final PluginManager pluginManager = new JarPluginManager(); // load the plugins pluginManager.loadPlugins(); diff --git a/demo/app/src/main/resources/log4j.properties b/demo/app/src/main/resources/log4j.properties index 0454ba2..692e39b 100644 --- a/demo/app/src/main/resources/log4j.properties +++ b/demo/app/src/main/resources/log4j.properties @@ -5,7 +5,7 @@ log4j.rootLogger=DEBUG, Console # log4j.logger.ro.fortsoft.pf4j=DEBUG, Console # !!! Put the bellow classes on level TRACE when you are in trouble -log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=WARN, Console +log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=DEBUG, Console log4j.logger.ro.fortsoft.pf4j.AbstractExtensionFinder=DEBUG, Console log4j.additivity.ro.fortsoft.pf4j=false log4j.additivity.ro.fortsoft.pf4j.PluginClassLoader=false diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java new file mode 100644 index 0000000..6087ec4 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java @@ -0,0 +1,800 @@ +/* + * Copyright 2016 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License 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 com.github.zafarkhaja.semver.Version; +import com.github.zafarkhaja.semver.expr.Expression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +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 java.util.Set; + +/** + * This class implements the boilerplate plugin code that any {@link PluginManager} + * implementation would have to support. + * It helps cut the noise out of the subclass that handles plugin management. + * + * @author Decebal Suiu + */ +public abstract class AbstractPluginManager implements PluginManager { + + private static final Logger log = LoggerFactory.getLogger(AbstractPluginManager.class); + + private Path pluginsRoot; + + private ExtensionFinder extensionFinder; + + private PluginDescriptorFinder pluginDescriptorFinder; + + /* + * A map of plugins this manager is responsible for (the key is the 'pluginId'). + */ + protected Map plugins; + + /* + * A map of plugin class loaders (he key is the 'pluginId'). + */ + private Map pluginClassLoaders; + + /* + * A list with unresolved plugins (unresolved dependency). + */ + private List unresolvedPlugins; + + /** + * A list with resolved plugins (resolved dependency). + */ + private List resolvedPlugins; + + /* + * A list with started plugins. + */ + private List startedPlugins; + + /* + * The registered {@link PluginStateListener}s. + */ + private List pluginStateListeners; + + /* + * Cache value for the runtime mode. + * No need to re-read it because it wont change at runtime. + */ + private RuntimeMode runtimeMode; + + /* + * The system version used for comparisons to the plugin requires attribute. + */ + private Version systemVersion = Version.forIntegers(0, 0, 0); + + /** + * A relation between 'pluginPath' and 'pluginId' + */ + private Map pathToIdMap; + + private PluginRepository pluginRepository; + private PluginFactory pluginFactory; + private ExtensionFactory extensionFactory; + private PluginStatusProvider pluginStatusProvider; + private DependencyResolver dependencyResolver; + private PluginLoader pluginLoader; + + /** + * The plugins root is supplied by {@code System.getProperty("pf4j.pluginsDir", "plugins")}. + */ + public AbstractPluginManager() { + initialize(); + } + + /** + * Constructs {@code DefaultPluginManager} which the given plugins root. + * + * @param pluginsRoot the root to search for plugins + */ + public AbstractPluginManager(Path pluginsRoot) { + this.pluginsRoot = pluginsRoot; + + initialize(); + } + + @Override + public void setSystemVersion(Version version) { + systemVersion = version; + } + + @Override + public Version getSystemVersion() { + return systemVersion; + } + + /** + * Returns a copy of plugins. + * + * @return + */ + @Override + public List getPlugins() { + return new ArrayList<>(plugins.values()); + } + + /** + * Returns a copy of plugins with that state. + * + * @param pluginState + * @return + */ + @Override + public List getPlugins(PluginState pluginState) { + List plugins = new ArrayList<>(); + for (PluginWrapper plugin : getPlugins()) { + if (pluginState.equals(plugin.getPluginState())) { + plugins.add(plugin); + } + } + + return plugins; + } + + @Override + public List getResolvedPlugins() { + return resolvedPlugins; + } + + @Override + public List getUnresolvedPlugins() { + return unresolvedPlugins; + } + + @Override + public List getStartedPlugins() { + return startedPlugins; + } + + @Override + public PluginWrapper getPlugin(String pluginId) { + return plugins.get(pluginId); + } + + @Override + public String loadPlugin(Path pluginPath) { + if ((pluginPath == null) || Files.notExists(pluginPath)) { + throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginPath)); + } + + log.debug("Loading plugin from '{}'", pluginPath); + + try { + PluginWrapper pluginWrapper = loadPluginFromPath(pluginPath); + // TODO uninstalled plugin dependencies? + getUnresolvedPlugins().remove(pluginWrapper); + getResolvedPlugins().add(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, null)); + + return pluginWrapper.getDescriptor().getPluginId(); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + + return null; + } + + /** + * Load plugins. + */ + @Override + public void loadPlugins() { + log.debug("Lookup plugins in '{}'", pluginsRoot); + // check for plugins root + if (Files.notExists(pluginsRoot) || !Files.isDirectory(pluginsRoot)) { + log.error("No '{}' root", pluginsRoot); + return; + } + + // get all plugin paths from repository + List pluginPaths = pluginRepository.getPluginPaths(); + + // check for no plugins + if (pluginPaths.isEmpty()) { + log.info("No plugins"); + return; + } + + log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths); + + // load plugins from plugin paths + // TODO + for (Path pluginPath : pluginPaths) { + try { + loadPluginFromPath(pluginPath); + } 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 pluginState = stopPlugin(pluginId); + if (PluginState.STARTED == pluginState) { + return false; + } + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginDescriptor descriptor = pluginWrapper.getDescriptor(); + List dependencies = descriptor.getDependencies(); + for (PluginDependency dependency : dependencies) { + if (!unloadPlugin(dependency.getPluginId())) { + return false; + } + } + + // remove the plugin + plugins.remove(pluginId); + getResolvedPlugins().remove(pluginWrapper); + pathToIdMap.remove(pluginWrapper.getPluginPath()); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + + // remove the classloader + Map pluginClassLoaders = getPluginClassLoaders(); + if (pluginClassLoaders.containsKey(pluginId)) { + ClassLoader classLoader = pluginClassLoaders.remove(pluginId); + if (classLoader instanceof Closeable) { + try { + ((Closeable) classLoader).close(); + } catch (IOException e) { + log.error("Cannot close classloader", 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 pluginWrapper = getPlugin(pluginId); + PluginState pluginState = stopPlugin(pluginId); + if (PluginState.STARTED == pluginState) { + 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; + } + + Path pluginPath = pluginWrapper.getPluginPath(); + + pluginRepository.deletePluginPath(pluginPath); + + return true; + } + + /** + * Start all active plugins. + */ + @Override + public void startPlugins() { + for (PluginWrapper pluginWrapper : resolvedPlugins) { + PluginState pluginState = pluginWrapper.getPluginState(); + if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { + try { + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + startedPlugins.add(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } 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 = getPlugin(pluginId); + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STARTED == pluginState) { + log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + return PluginState.STARTED; + } + + if (PluginState.DISABLED == pluginState) { + // automatically enable plugin on manual plugin start + if (!enablePlugin(pluginId)) { + return pluginState; + } + } + + 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); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } 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 itr = startedPlugins.iterator(); + while (itr.hasNext()) { + PluginWrapper pluginWrapper = itr.next(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STARTED == pluginState) { + try { + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + pluginWrapper.getPlugin().stop(); + pluginWrapper.setPluginState(PluginState.STOPPED); + itr.remove(); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + } + } + + /** + * Stop the specified plugin and it's dependencies. + */ + @Override + public PluginState stopPlugin(String pluginId) { + return stopPlugin(pluginId, true); + } + + private PluginState stopPlugin(String pluginId, boolean stopDependents) { + if (!plugins.containsKey(pluginId)) { + throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); + } + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STOPPED == pluginState) { + log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + return PluginState.STOPPED; + } + + // test for disabled plugin + if (PluginState.DISABLED == pluginState) { + // do nothing + return pluginState; + } + + if (stopDependents) { + List dependents = dependencyResolver.getDependents(pluginId); + while (!dependents.isEmpty()) { + String dependent = dependents.remove(0); + stopPlugin(dependent, false); + dependents.addAll(0, dependencyResolver.getDependents(dependent)); + } + } + + try { + log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + pluginWrapper.getPlugin().stop(); + pluginWrapper.setPluginState(PluginState.STOPPED); + startedPlugins.remove(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + + return pluginWrapper.getPluginState(); + } + + @Override + public boolean disablePlugin(String pluginId) { + if (!plugins.containsKey(pluginId)) { + throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); + } + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.DISABLED == pluginState) { + log.debug("Already disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + return true; + } + + if (PluginState.STOPPED == stopPlugin(pluginId)) { + pluginWrapper.setPluginState(PluginState.DISABLED); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); + + if (!pluginStatusProvider.disablePlugin(pluginId)) { + return false; + } + + log.info("Disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + + return true; + } + + return false; + } + + @Override + public boolean enablePlugin(String pluginId) { + if (!plugins.containsKey(pluginId)) { + throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); + } + + PluginWrapper pluginWrapper = getPlugin(pluginId); + if (!isPluginValid(pluginWrapper)) { + log.warn("Plugin '{}:{}' can not be enabled", pluginWrapper.getPluginId(), + pluginWrapper.getDescriptor().getVersion()); + return false; + } + + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.DISABLED != pluginState) { + log.debug("Plugin '{}:{}' is not disabled", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + return true; + } + + if (!pluginStatusProvider.enablePlugin(pluginId)) { + return false; + } + + pluginWrapper.setPluginState(PluginState.CREATED); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + + log.info("Enabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + + return true; + } + + /** + * Get plugin class loader for this path. + */ + @Override + public ClassLoader getPluginClassLoader(String pluginId) { + return pluginClassLoaders.get(pluginId); + } + + @Override + public List getExtensions(Class type) { + List> extensionsWrapper = extensionFinder.find(type); + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getExtension()); + } + + return extensions; + } + + @Override + public List getExtensions(Class type, String pluginId) { + List> extensionsWrapper = extensionFinder.find(type, pluginId); + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getExtension()); + } + + return extensions; + } + + @Override + @SuppressWarnings("unchecked") + public List getExtensions(String pluginId) { + List extensionsWrapper = extensionFinder.find(pluginId); + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getExtension()); + } + + return extensions; + } + + @Override + public Set getExtensionClassNames(String pluginId) { + return extensionFinder.findClassNames(pluginId); + } + + @Override + public ExtensionFactory getExtensionFactory() { + return extensionFactory; + } + + // TODO remove + public PluginLoader getPluginLoader() { + return pluginLoader; + } + + public Path getPluginsRoot() { + return pluginsRoot; + } + + @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); + } + + return runtimeMode; + } + + @Override + public PluginWrapper whichPlugin(Class clazz) { + ClassLoader classLoader = clazz.getClassLoader(); + for (PluginWrapper plugin : resolvedPlugins) { + if (plugin.getPluginClassLoader() == classLoader) { + return plugin; + } + } + + return null; + } + + @Override + public synchronized void addPluginStateListener(PluginStateListener listener) { + pluginStateListeners.add(listener); + } + + @Override + public synchronized void removePluginStateListener(PluginStateListener listener) { + pluginStateListeners.remove(listener); + } + + public Version getVersion() { + String version = null; + + Package pf4jPackage = getClass().getPackage(); + if (pf4jPackage != null) { + version = pf4jPackage.getImplementationVersion(); + if (version == null) { + version = pf4jPackage.getSpecificationVersion(); + } + } + + return (version != null) ? Version.valueOf(version) : Version.forIntegers(0, 0, 0); + } + + protected abstract PluginRepository createPluginRepository(); + + protected abstract PluginFactory createPluginFactory(); + + protected abstract ExtensionFactory createExtensionFactory(); + + protected abstract PluginDescriptorFinder createPluginDescriptorFinder(); + + protected abstract ExtensionFinder createExtensionFinder(); + + protected abstract PluginStatusProvider createPluginStatusProvider(); + + protected abstract PluginLoader createPluginLoader(); + + protected PluginDescriptorFinder getPluginDescriptorFinder() { + return pluginDescriptorFinder; + } + + protected PluginFactory getPluginFactory() { + return pluginFactory; + } + + protected Map getPluginClassLoaders() { + return pluginClassLoaders; + } + + protected void initialize() { + plugins = new HashMap<>(); + pluginClassLoaders = new HashMap<>(); + unresolvedPlugins = new ArrayList<>(); + resolvedPlugins = new ArrayList<>(); + startedPlugins = new ArrayList<>(); + + pluginStateListeners = new ArrayList<>(); + pathToIdMap = new HashMap<>(); + + if (pluginsRoot == null) { + pluginsRoot = createPluginsRoot(); + } + + System.setProperty("pf4j.pluginsDir", pluginsRoot.toString()); + + dependencyResolver = new DependencyResolver(); + + pluginRepository = createPluginRepository(); + pluginFactory = createPluginFactory(); + extensionFactory = createExtensionFactory(); + pluginDescriptorFinder = createPluginDescriptorFinder(); + extensionFinder = createExtensionFinder(); + pluginStatusProvider = createPluginStatusProvider(); + pluginLoader = createPluginLoader(); + } + + /** + * Add the possibility to override the plugins root. + * If a "pf4j.pluginsDir" system property is defined than this method returns + * that root. + * If getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a + * DEVELOPMENT_PLUGINS_DIRECTORY ("../plugins") is returned else this method returns + * DEFAULT_PLUGINS_DIRECTORY ("plugins"). + * @return + */ + protected Path createPluginsRoot() { + String pluginsDir = System.getProperty("pf4j.pluginsDir"); + if (pluginsDir == null) { + if (isDevelopment()) { + pluginsDir = "../plugins"; + } else { + pluginsDir = "plugins"; + } + } + + return Paths.get(pluginsDir); + } + + protected boolean isPluginValid(PluginWrapper pluginWrapper) { + Expression requires = pluginWrapper.getDescriptor().getRequires(); + Version system = getSystemVersion(); + if (requires.interpret(system)) { + return true; + } + + log.warn("Plugin '{}:{}' requires a minimum system version of {}", + pluginWrapper.getPluginId(), + pluginWrapper.getDescriptor().getVersion(), + requires); + + return false; + } + + protected boolean isPluginDisabled(String pluginId) { + return pluginStatusProvider.isPluginDisabled(pluginId); + } + + protected void resolvePlugins() throws PluginException { + resolveDependencies(); + } + + protected void resolveDependencies() throws PluginException { + dependencyResolver.resolve(unresolvedPlugins); + resolvedPlugins = dependencyResolver.getSortedPlugins(); + for (PluginWrapper pluginWrapper : resolvedPlugins) { + unresolvedPlugins.remove(pluginWrapper); + log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); + } + } + + protected synchronized void firePluginStateEvent(PluginStateEvent event) { + for (PluginStateListener listener : pluginStateListeners) { + log.debug("Fire '{}' to '{}'", event, listener); + listener.pluginStateChanged(event); + } + } + + protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { + // 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 = getPluginDescriptorFinder().find(pluginPath); + log.debug("Descriptor {}", pluginDescriptor); + String pluginClassName = pluginDescriptor.getPluginClass(); + log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); + + // load plugin + log.debug("Loading plugin '{}'", pluginPath); + ClassLoader pluginClassLoader = getPluginLoader().loadPlugin(pluginPath, pluginDescriptor); + log.debug("Loaded plugin '{}' with class loader '{}'", pluginPath, pluginClassLoader); + + // create the plugin wrapper + log.debug("Creating wrapper for plugin '{}'", pluginPath); + PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader); + pluginWrapper.setPluginFactory(getPluginFactory()); + pluginWrapper.setRuntimeMode(getRuntimeMode()); + + // test for disabled plugin + if (isPluginDisabled(pluginDescriptor.getPluginId())) { + log.info("Plugin '{}' is disabled", pluginPath); + pluginWrapper.setPluginState(PluginState.DISABLED); + } + + // validate the plugin + if (!isPluginValid(pluginWrapper)) { + log.info("Plugin '{}' is disabled", pluginPath); + pluginWrapper.setPluginState(PluginState.DISABLED); + } + + log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); + + String pluginId = pluginDescriptor.getPluginId(); + + // add plugin to the list with plugins + plugins.put(pluginId, pluginWrapper); + getUnresolvedPlugins().add(pluginWrapper); + + // add plugin class loader to the list with class loaders + getPluginClassLoaders().put(pluginId, pluginClassLoader); + + return pluginWrapper; + } + + // TODO add this method in PluginManager as default method for Java 8. + protected boolean isDevelopment() { + return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode()); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java new file mode 100644 index 0000000..dd9fc80 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License 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.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Decebal Suiu + * @author Mário Franco + */ +public class BasePluginRepository implements PluginRepository { + + protected final Path pluginsRoot; + protected FileFilter filter; + + public BasePluginRepository(Path pluginsRoot) { + this.pluginsRoot = pluginsRoot; + } + + public BasePluginRepository(Path pluginsRoot, FileFilter filter) { + this.pluginsRoot = pluginsRoot; + this.filter = filter; + } + + public void setFilter(FileFilter filter) { + this.filter = filter; + } + + @Override + public List getPluginPaths() { + File[] files = pluginsRoot.toFile().listFiles(filter); + + if ((files == null) || files.length == 0) { + return Collections.emptyList(); + } + + List paths = new ArrayList<>(files.length); + for (File file : files) { + paths.add(file.toPath()); + } + + return paths; + } + + @Override + public boolean deletePluginPath(Path pluginPath) { + try { + return Files.deleteIfExists(pluginPath); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java index 6556f05..b0341ea 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java @@ -15,7 +15,7 @@ */ package ro.fortsoft.pf4j; -import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -32,19 +32,19 @@ public class CompoundPluginRepository implements PluginRepository { } @Override - public List getPluginArchives() { - List file = new ArrayList<>(); + public List getPluginPaths() { + List paths = new ArrayList<>(); for (PluginRepository repository : repositories) { - file.addAll(repository.getPluginArchives()); + paths.addAll(repository.getPluginPaths()); } - return file; + return paths; } @Override - public boolean deletePluginArchive(String pluginPath) { + public boolean deletePluginPath(Path pluginPath) { for (PluginRepository repository : repositories) { - if (repository.deletePluginArchive(pluginPath)) { + if (repository.deletePluginPath(pluginPath)) { return true; } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/CyclicDependencyException.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java similarity index 68% rename from pf4j/src/main/java/ro/fortsoft/pf4j/CyclicDependencyException.java rename to pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java index 16b6e56..71631c1 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/CyclicDependencyException.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Decebal Suiu + * Copyright 2016 Decebal Suiu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,17 @@ package ro.fortsoft.pf4j; /** - * CyclicDependencyException will be thrown if a cyclic dependency is detected. + * The default values are {@code classes} and {@code lib}. * * @author Decebal Suiu */ -class CyclicDependencyException extends PluginException { +public class DefaultPluginClasspath extends PluginClasspath { - private static final long serialVersionUID = 1L; + public DefaultPluginClasspath() { + super(); - public CyclicDependencyException(String message) { - super(message); - } + addClassesDirectories("classes"); + addLibDirectories("lib"); + } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java index 84abcba..e58aa05 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java @@ -15,16 +15,47 @@ */ package ro.fortsoft.pf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.jar.Manifest; + /** - * The default implementation for PluginDescriptorFinder. - * Now, this class it's a "link" to {@link ro.fortsoft.pf4j.ManifestPluginDescriptorFinder}. + * The default implementation for {@link PluginDescriptorFinder}. + * Now, this class it's a "link" to {@link ManifestPluginDescriptorFinder}. * * @author Decebal Suiu */ public class DefaultPluginDescriptorFinder extends ManifestPluginDescriptorFinder { - public DefaultPluginDescriptorFinder(PluginClasspath pluginClasspath) { - super(pluginClasspath); - } + private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); + + private PluginClasspath pluginClasspath; + + public DefaultPluginDescriptorFinder(PluginClasspath pluginClasspath) { + this.pluginClasspath = pluginClasspath; + } + + @Override + public Manifest readManifest(Path pluginPath) throws PluginException { + // TODO it's ok with first classes root? Another idea is to specify in PluginClasspath the folder. + String classes = pluginClasspath.getClassesDirectories().get(0); + Path manifestPath = pluginPath.resolve(Paths.get(classes,"/META-INF/MANIFEST.MF")); + log.debug("Lookup plugin descriptor in '{}'", manifestPath); + if (Files.notExists(manifestPath)) { + throw new PluginException("Cannot find '" + manifestPath + "' path"); + } + + try (InputStream input = Files.newInputStream(manifestPath)) { + return new Manifest(input); + } catch (IOException e) { + throw new PluginException(e.getMessage(), e); + } + } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java index 76c2760..9bc3173 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java @@ -22,8 +22,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; /** - * The default implementation for PluginFactory. - * It uses Class.newInstance() method. + * The default implementation for {@link PluginFactory}. + * It uses {@link Class#newInstance()} method. * * @author Decebal Suiu */ diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java new file mode 100644 index 0000000..88bbbef --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License 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 ro.fortsoft.pf4j.util.FileUtils; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +/** + * Load all information needed by a plugin. + * This means add to classpath all jar files from {@code lib} directory + * and all class files from {@code classes}. + * + * @author Decebal Suiu + */ +public class DefaultPluginLoader implements PluginLoader { + + protected PluginManager pluginManager; + protected PluginClasspath pluginClasspath; + + public DefaultPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { + this.pluginManager = pluginManager; + this.pluginClasspath = pluginClasspath; + } + + @Override + public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { + PluginClassLoader pluginClassLoader = createPluginClassLoader(pluginPath, pluginDescriptor); + + loadClasses(pluginPath, pluginClassLoader); + loadJars(pluginPath, pluginClassLoader); + + return pluginClassLoader; + } + + protected PluginClassLoader createPluginClassLoader(Path pluginPath, PluginDescriptor pluginDescriptor) { + return new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); + } + + /** + * Add all {@code *.class} files from {@code classes} directories to plugin class loader. + */ + protected void loadClasses(Path pluginPath, PluginClassLoader pluginClassLoader) { + for (String directory : pluginClasspath.getClassesDirectories()) { + File file = pluginPath.resolve(directory).toFile(); + if (file.exists() && file.isDirectory()) { + pluginClassLoader.addFile(file); + } + } + } + + /** + * Add all {@code *.jar} files from {@code lib} directories to plugin class loader. + */ + protected void loadJars(Path pluginPath, PluginClassLoader pluginClassLoader) { + for (String libDirectory : pluginClasspath.getLibDirectories()) { + File file = pluginPath.resolve(libDirectory).toFile(); + List jars = FileUtils.getJars(file); + for (File jar : jars) { + pluginClassLoader.addFile(jar); + } + } + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index 60b09e8..81bce1b 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -15,882 +15,95 @@ */ package ro.fortsoft.pf4j; -import com.github.zafarkhaja.semver.Version; -import com.github.zafarkhaja.semver.expr.Expression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.AndFileFilter; -import ro.fortsoft.pf4j.util.DirectoryFileFilter; -import ro.fortsoft.pf4j.util.FileUtils; -import ro.fortsoft.pf4j.util.HiddenFilter; -import ro.fortsoft.pf4j.util.NameFileFilter; -import ro.fortsoft.pf4j.util.NotFileFilter; -import ro.fortsoft.pf4j.util.OrFileFilter; -import ro.fortsoft.pf4j.util.Unzip; -import ro.fortsoft.pf4j.util.ZipFileFilter; 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 java.util.Set; +import java.nio.file.Path; /** - * Default implementation of the PluginManager interface. + * Default implementation of the {@link PluginManager} interface. * * @author Decebal Suiu */ -public class DefaultPluginManager implements PluginManager { +public class DefaultPluginManager extends AbstractPluginManager { 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"; - - protected File pluginsDirectory; - - protected ExtensionFinder extensionFinder; - - protected PluginDescriptorFinder pluginDescriptorFinder; - protected PluginClasspath pluginClasspath; - /** - * A map of plugins this manager is responsible for (the key is the 'pluginId'). - */ - protected Map plugins; - - /** - * A map of plugin class loaders (he key is the 'pluginId'). - */ - protected Map pluginClassLoaders; - - /** - * A relation between 'pluginPath' and 'pluginId' - */ - protected Map pathToIdMap; - - /** - * A list with unresolved plugins (unresolved dependency). - */ - protected List unresolvedPlugins; - - /** - * A list with resolved plugins (resolved dependency). - */ - protected List resolvedPlugins; - - /** - * A list with started plugins. - */ - protected List startedPlugins; - - /** - * The registered {@link PluginStateListener}s. - */ - protected List pluginStateListeners; - - /** - * Cache value for the runtime mode. No need to re-read it because it wont change at - * runtime. - */ - protected RuntimeMode runtimeMode; - - /** - * The system version used for comparisons to the plugin requires attribute. - */ - protected Version systemVersion = Version.forIntegers(0, 0, 0); - - protected PluginFactory pluginFactory; - protected ExtensionFactory extensionFactory; - protected PluginStatusProvider pluginStatusProvider; - protected DependencyResolver dependencyResolver; - - /** - * The plugins repository. - */ - protected PluginRepository pluginRepository; - - /** - * 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 void setSystemVersion(Version version) { - systemVersion = version; - } - - @Override - public Version getSystemVersion() { - return systemVersion; - } - - @Override - public List getPlugins() { - return new ArrayList<>(plugins.values()); - } - - @Override - public List getPlugins(PluginState pluginState) { - List plugins= new ArrayList<>(); - for (PluginWrapper plugin : getPlugins()) { - if (pluginState.equals(plugin.getPluginState())) { - plugins.add(plugin); - } - } - - return plugins; - } - - @Override - public List getResolvedPlugins() { - return resolvedPlugins; - } - - @Override - public List getUnresolvedPlugins() { - return unresolvedPlugins; - } - - @Override - public List getStartedPlugins() { - return startedPlugins; - } - - @Override - public PluginWrapper getPlugin(String pluginId) { - return plugins.get(pluginId); - } - - @Override - public String loadPlugin(File pluginArchiveFile) { - if ((pluginArchiveFile == null) || !pluginArchiveFile.exists()) { - throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginArchiveFile)); - } - - log.debug("Loading plugin from '{}'", 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); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, null)); - - 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) { - PluginState pluginState = pluginWrapper.getPluginState(); - if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { - try { - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); - startedPlugins.add(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } 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 = getPlugin(pluginId); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STARTED == pluginState) { - log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - return PluginState.STARTED; - } - - if (PluginState.DISABLED == pluginState) { - // automatically enable plugin on manual plugin start - if (!enablePlugin(pluginId)) { - return pluginState; - } - } - - 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); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } 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 itr = startedPlugins.iterator(); - while (itr.hasNext()) { - PluginWrapper pluginWrapper = itr.next(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STARTED == pluginState) { - try { - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - itr.remove(); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - } - } - - /** - * Stop the specified plugin and it's dependencies. - */ - @Override - public PluginState stopPlugin(String pluginId) { - return stopPlugin(pluginId, true); + super(); } - private PluginState stopPlugin(String pluginId, boolean stopDependents) { - if (!plugins.containsKey(pluginId)) { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STOPPED == pluginState) { - log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - return PluginState.STOPPED; - } - - // test for disabled plugin - if (PluginState.DISABLED == pluginState) { - // do nothing - return pluginState; - } - - if (stopDependents) { - List dependents = dependencyResolver.getDependents(pluginId); - while (!dependents.isEmpty()) { - String dependent = dependents.remove(0); - stopPlugin(dependent, false); - dependents.addAll(0, dependencyResolver.getDependents(dependent)); - } - } - - try { - log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - startedPlugins.remove(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } 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 - List pluginArchives = pluginRepository.getPluginArchives(); - for (File archiveFile : pluginArchives) { - try { - expandPluginArchive(archiveFile); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - - // check for no plugins - AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); - pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter())); - 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 pluginState = stopPlugin(pluginId); - if (PluginState.STARTED == pluginState) { - return false; - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginDescriptor descriptor = pluginWrapper.getDescriptor(); - List 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()); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - - // remove the classloader - if (pluginClassLoaders.containsKey(pluginId)) { - PluginClassLoader classLoader = pluginClassLoaders.remove(pluginId); - classLoader.dispose(); - } - - return true; - } catch (IllegalArgumentException e) { - // ignore not found exceptions because this method is recursive - } - - return false; - } - - @Override - public boolean disablePlugin(String pluginId) { - if (!plugins.containsKey(pluginId)) { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.DISABLED == pluginState) { - log.debug("Already disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - return true; - } - - if (PluginState.STOPPED == stopPlugin(pluginId)) { - pluginWrapper.setPluginState(PluginState.DISABLED); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); - - if (!pluginStatusProvider.disablePlugin(pluginId)) { - return false; - } - - log.info("Disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - - return true; - } - - return false; + @Deprecated + public DefaultPluginManager(File pluginsDir) { + this(pluginsDir.toPath()); } - @Override - public boolean enablePlugin(String pluginId) { - if (!plugins.containsKey(pluginId)) { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - if (!isPluginValid(pluginWrapper)) { - log.warn("Plugin '{}:{}' can not be enabled", pluginWrapper.getPluginId(), - pluginWrapper.getDescriptor().getVersion()); - return false; - } - - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.DISABLED != pluginState) { - log.debug("Plugin '{}:{}' is not disabled", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - return true; - } - - if (!pluginStatusProvider.enablePlugin(pluginId)) { - return false; - } - - pluginWrapper.setPluginState(PluginState.CREATED); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - - log.info("Enabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - - return true; + public DefaultPluginManager(Path pluginsRoot) { + super(pluginsRoot); } - @Override - public boolean deletePlugin(String pluginId) { - if (!plugins.containsKey(pluginId)) { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginState pluginState = stopPlugin(pluginId); - if (PluginState.STARTED == pluginState) { - 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, pluginWrapper.getPluginPath()); - - if (pluginFolder.exists()) { - FileUtils.delete(pluginFolder); - } - - pluginRepository.deletePluginArchive(pluginWrapper.getPluginPath()); - - return true; - } - /** - * Get plugin class loader for this path. - */ - @Override - public PluginClassLoader getPluginClassLoader(String pluginId) { - return pluginClassLoaders.get(pluginId); - } - - @Override - public List getExtensions(Class type) { - List> extensionsWrapper = extensionFinder.find(type); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } - - return extensions; - } - + * By default if {@link DefaultPluginManager#isDevelopment()} returns true + * than a {@link PropertiesPluginDescriptorFinder} is returned + * else this method returns {@link DefaultPluginDescriptorFinder}. + */ @Override - public List getExtensions(Class type, String pluginId) { - List> extensionsWrapper = extensionFinder.find(type, pluginId); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } - - return extensions; + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new DefaultPluginDescriptorFinder(pluginClasspath); } @Override - @SuppressWarnings("unchecked") - public List getExtensions(String pluginId) { - List extensionsWrapper = extensionFinder.find(pluginId); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } + protected ExtensionFinder createExtensionFinder() { + DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); + addPluginStateListener(extensionFinder); - return extensions; + return extensionFinder; } @Override - public Set getExtensionClassNames(String pluginId) { - return extensionFinder.findClassNames(pluginId); + protected PluginFactory createPluginFactory() { + return new DefaultPluginFactory(); } @Override - public ExtensionFactory getExtensionFactory() { - return extensionFactory; + protected ExtensionFactory createExtensionFactory() { + return new DefaultExtensionFactory(); } @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); - } - - return runtimeMode; - } - - @Override - public PluginWrapper whichPlugin(Class clazz) { - ClassLoader classLoader = clazz.getClassLoader(); - for (PluginWrapper plugin : resolvedPlugins) { - if (plugin.getPluginClassLoader() == classLoader) { - return plugin; - } - } - - return null; + protected PluginStatusProvider createPluginStatusProvider() { + return new DefaultPluginStatusProvider(getPluginsRoot()); } @Override - public synchronized void addPluginStateListener(PluginStateListener listener) { - pluginStateListeners.add(listener); + protected PluginRepository createPluginRepository() { + return new DefaultPluginRepository(getPluginsRoot(), isDevelopment()); } @Override - public synchronized void removePluginStateListener(PluginStateListener listener) { - pluginStateListeners.remove(listener); - } - - public Version getVersion() { - String version = null; - - Package pf4jPackage = getClass().getPackage(); - if (pf4jPackage != null) { - version = pf4jPackage.getImplementationVersion(); - if (version == null) { - version = pf4jPackage.getSpecificationVersion(); - } - } - - return (version != null) ? Version.valueOf(version) : Version.forIntegers(0, 0, 0); + protected PluginLoader createPluginLoader() { + return new DefaultPluginLoader(this, pluginClasspath); } /** - * 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() { - DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); - addPluginStateListener(extensionFinder); - - return extensionFinder; - } - - /** - * Add the possibility to override the PluginClassPath. - * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a - * DevelopmentPluginClasspath is returned else this method returns - * PluginClasspath. + * By default if {@link DefaultPluginManager#isDevelopment()} returns true + * than a {@link DevelopmentPluginClasspath} is returned + * else this method returns {@link DefaultPluginClasspath}. */ protected PluginClasspath createPluginClasspath() { - if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { - return new DevelopmentPluginClasspath(); - } - - return new PluginClasspath(); - } - - protected PluginStatusProvider createPluginStatusProvider() { - return new DefaultPluginStatusProvider(pluginsDirectory); - } - - protected PluginRepository createPluginRepository() { - return new DefaultPluginRepository(pluginsDirectory, new ZipFileFilter()); - } - - protected boolean isPluginDisabled(String pluginId) { - return pluginStatusProvider.isPluginDisabled(pluginId); - } - - protected boolean isPluginValid(PluginWrapper pluginWrapper) { - Expression requires = pluginWrapper.getDescriptor().getRequires(); - Version system = getSystemVersion(); - if (requires.interpret(system)) { - return true; - } - - log.warn("Plugin '{}:{}' requires a minimum system version of {}", - pluginWrapper.getPluginId(), - pluginWrapper.getDescriptor().getVersion(), - requires); - - return false; - } - - protected FileFilter createHiddenPluginFilter() { - OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); - - if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { - hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); - } - - return hiddenPluginFilter; - } - - /** - * 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); - } - - /** - * Add the possibility to override the PluginFactory.. - */ - protected PluginFactory createPluginFactory() { - return new DefaultPluginFactory(); - } - - /** - * Add the possibility to override the ExtensionFactory. - */ - protected ExtensionFactory createExtensionFactory() { - return new DefaultExtensionFactory(); - } - - /** - * Add the possibility to override the PluginClassLoader. - */ - protected PluginClassLoader createPluginClassLoader(PluginDescriptor pluginDescriptor) { - return new PluginClassLoader(this, pluginDescriptor, getClass().getClassLoader()); + return isDevelopment() ? new DevelopmentPluginClasspath() : new DefaultPluginClasspath(); } - private void initialize() { - plugins = new HashMap<>(); - pluginClassLoaders = new HashMap<>(); - pathToIdMap = new HashMap<>(); - unresolvedPlugins = new ArrayList<>(); - resolvedPlugins = new ArrayList<>(); - startedPlugins = new ArrayList<>(); - - pluginStateListeners = new ArrayList<>(); - - dependencyResolver = new DependencyResolver(); - - log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); - + @Override + protected void initialize() { pluginClasspath = createPluginClasspath(); - pluginFactory = createPluginFactory(); - extensionFactory = createExtensionFactory(); - pluginDescriptorFinder = createPluginDescriptorFinder(); - extensionFinder = createExtensionFinder(); - pluginStatusProvider = createPluginStatusProvider(); - pluginRepository = createPluginRepository(); - - try { - pluginsDirectory = pluginsDirectory.getCanonicalFile(); - } 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); - - // load plugin - log.debug("Loading plugin '{}'", pluginPath); - PluginClassLoader pluginClassLoader = createPluginClassLoader(pluginDescriptor); - log.debug("Created class loader '{}'", pluginClassLoader); - PluginLoader pluginLoader = new PluginLoader(pluginDirectory, pluginClassLoader, pluginClasspath); - pluginLoader.load(); - log.debug("Loaded plugin '{}'", pluginPath); - - // create the plugin wrapper - log.debug("Creating wrapper for plugin '{}'", pluginPath); - PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader); - pluginWrapper.setPluginFactory(pluginFactory); - pluginWrapper.setRuntimeMode(getRuntimeMode()); - - // test for disabled plugin - if (isPluginDisabled(pluginDescriptor.getPluginId())) { - log.info("Plugin '{}' is disabled", pluginPath); - pluginWrapper.setPluginState(PluginState.DISABLED); - } - - // validate the plugin - if (!isPluginValid(pluginWrapper)) { - log.info("Plugin '{}' is disabled", pluginPath); - pluginWrapper.setPluginState(PluginState.DISABLED); - } - log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); + super.initialize(); - 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 - 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.resolve(unresolvedPlugins); - resolvedPlugins = dependencyResolver.getSortedPlugins(); - for (PluginWrapper pluginWrapper : resolvedPlugins) { - unresolvedPlugins.remove(pluginWrapper); - log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); - } + log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); } - private synchronized void firePluginStateEvent(PluginStateEvent event) { - for (PluginStateListener listener : pluginStateListeners) { - log.debug("Fire '{}' to '{}'", event, listener); - listener.pluginStateChanged(event); - } - } - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java index 327e4fe..6efb205 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java @@ -15,56 +15,108 @@ */ package ro.fortsoft.pf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ro.fortsoft.pf4j.util.AndFileFilter; +import ro.fortsoft.pf4j.util.DirectoryFileFilter; import ro.fortsoft.pf4j.util.FileUtils; +import ro.fortsoft.pf4j.util.HiddenFilter; +import ro.fortsoft.pf4j.util.NameFileFilter; +import ro.fortsoft.pf4j.util.NotFileFilter; +import ro.fortsoft.pf4j.util.OrFileFilter; +import ro.fortsoft.pf4j.util.Unzip; +import ro.fortsoft.pf4j.util.ZipFileFilter; import java.io.File; import java.io.FileFilter; -import java.util.Arrays; -import java.util.Collections; +import java.io.IOException; +import java.nio.file.Path; import java.util.List; /** * @author Decebal Suiu - * @author Mário Franco */ -public class DefaultPluginRepository implements PluginRepository { +public class DefaultPluginRepository extends BasePluginRepository { - private final File directory; - private final FileFilter filter; + private static final Logger log = LoggerFactory.getLogger(DefaultPluginRepository.class); - public DefaultPluginRepository(File directory, FileFilter filter) { - this.directory = directory; - this.filter = filter; + public DefaultPluginRepository(Path pluginsRoot, boolean development) { + super(pluginsRoot); + + AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); + pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); + setFilter(pluginsFilter); } @Override - public List getPluginArchives() { - File[] files = directory.listFiles(filter); + public List getPluginPaths() { + // expand plugins zip files + File[] pluginZips = pluginsRoot.toFile().listFiles(new ZipFileFilter()); + if ((pluginZips != null) && pluginZips.length > 0) { + for (File pluginZip : pluginZips) { + try { + expandPluginZip(pluginZip); + } catch (IOException e) { + log.error("Cannot expand plugin zip '{}'", pluginZip); + log.error(e.getMessage(), e); + } + } + } - return (files != null) ? Arrays.asList(files) : Collections.emptyList(); + return super.getPluginPaths(); } @Override - public boolean deletePluginArchive(String pluginPath) { - File[] files = directory.listFiles(filter); - if (files != null) { - File pluginArchive = null; - // strip prepended "/" from the plugin path - String dirName = pluginPath.substring(1); - // find the zip file that matches the plugin path - for (File archive : files) { - String name = archive.getName().substring(0, archive.getName().lastIndexOf('.')); - if (name.equals(dirName)) { - pluginArchive = archive; - break; - } - } - if (pluginArchive != null && pluginArchive.exists()) { - return FileUtils.delete(pluginArchive); + public boolean deletePluginPath(Path pluginPath) { + // TODO remove plugin zip ? + return super.deletePluginPath(pluginPath); + } + + protected FileFilter createHiddenPluginFilter(boolean development) { + OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); + + if (development) { + hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); + } + + return hiddenPluginFilter; + } + + /** + * Unzip a plugin zip file in a directory that has the same name as the zip file + * and it's relative to {@code pluginsRoot}. + * For example if the zip file is {@code my-plugin.zip} then the resulted directory + * is {@code my-plugin}. + * + * @param pluginZip + * @return + * @throws IOException + */ + private File expandPluginZip(File pluginZip) throws IOException { + String fileName = pluginZip.getName(); + long pluginZipDate = pluginZip.lastModified(); + String pluginName = fileName.substring(0, fileName.length() - 4); + File pluginDirectory = pluginsRoot.resolve(pluginName).toFile(); + // check if exists root or the '.zip' file is "newer" than root + if (!pluginDirectory.exists() || (pluginZipDate > pluginDirectory.lastModified())) { + log.debug("Expand plugin zip '{}' in '{}'", pluginZip, pluginDirectory); + + // do not overwrite an old version, remove it + if (pluginDirectory.exists()) { + FileUtils.delete(pluginDirectory); } + + // create root for plugin + pluginDirectory.mkdirs(); + + // expand '.zip' file + Unzip unzip = new Unzip(); + unzip.setSource(pluginZip); + unzip.setDestination(pluginDirectory); + unzip.extract(); } - return false; + return pluginDirectory; } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java index 8522e9c..f5cb514 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java @@ -15,16 +15,16 @@ */ package ro.fortsoft.pf4j; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.fortsoft.pf4j.util.FileUtils; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + /** - * The default implementation for PluginStatusProvider. + * The default implementation for {@link PluginStatusProvider}. * * @author Decebal Suiu * @author Mário Franco @@ -33,24 +33,25 @@ public class DefaultPluginStatusProvider implements PluginStatusProvider { private static final Logger log = LoggerFactory.getLogger(DefaultPluginStatusProvider.class); - private final File pluginsDirectory; + private final Path pluginsRoot; + + private List enabledPlugins; + private List disabledPlugins; - private List enabledPlugins = new ArrayList<>(); - private List disabledPlugins = new ArrayList<>(); + public DefaultPluginStatusProvider(Path pluginsRoot) { + this.pluginsRoot = pluginsRoot; - public DefaultPluginStatusProvider(File pluginsDirectory) { - this.pluginsDirectory = pluginsDirectory; initialize(); } private void initialize() { 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); + enabledPlugins = FileUtils.readLines(pluginsRoot.resolve("enabled.txt").toFile(), 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); + disabledPlugins = FileUtils.readLines(pluginsRoot.resolve("disabled.txt").toFile(), true); log.info("Disabled plugins: {}", disabledPlugins); } catch (IOException e) { log.error(e.getMessage(), e); @@ -70,12 +71,13 @@ public class DefaultPluginStatusProvider implements PluginStatusProvider { public boolean disablePlugin(String pluginId) { if (disabledPlugins.add(pluginId)) { try { - FileUtils.writeLines(disabledPlugins, new File(pluginsDirectory, "disabled.txt")); + FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); } catch (IOException e) { log.error("Failed to disable plugin {}", pluginId, e); return false; } } + return true; } @@ -83,12 +85,13 @@ public class DefaultPluginStatusProvider implements PluginStatusProvider { public boolean enablePlugin(String pluginId) { try { if (disabledPlugins.remove(pluginId)) { - FileUtils.writeLines(disabledPlugins, new File(pluginsDirectory, "disabled.txt")); + FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); } } catch (IOException e) { log.error("Failed to enable plugin {}", pluginId, e); return false; } + return true; } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java index e281495..c26881c 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java @@ -71,7 +71,7 @@ public class DependencyResolver { List pluginsId = dependenciesGraph.reverseTopologicalSort(); if (pluginsId == null) { - throw new CyclicDependencyException("Cyclic dependencies !!!" + dependenciesGraph.toString()); + throw new PluginException("Cyclic dependencies !!!" + dependenciesGraph.toString()); } log.debug("Plugins order: {}", pluginsId); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java index 0109222..ea70f9a 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java @@ -16,24 +16,17 @@ package ro.fortsoft.pf4j; /** - * Overwrite classes directories to "target/classes" and lib directories to "target/lib". + * Overwrite classes directories to {@code target/classes} and lib directories to {@code 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); - } + addClassesDirectories("target/classes"); + addLibDirectories("target/lib"); + } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java new file mode 100644 index 0000000..b2442ff --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java @@ -0,0 +1,115 @@ +/* + * Copyright 2016 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License 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 ro.fortsoft.pf4j.util.AndFileFilter; +import ro.fortsoft.pf4j.util.DirectoryFileFilter; +import ro.fortsoft.pf4j.util.HiddenFilter; +import ro.fortsoft.pf4j.util.JarFileFilter; +import ro.fortsoft.pf4j.util.NameFileFilter; +import ro.fortsoft.pf4j.util.NotFileFilter; +import ro.fortsoft.pf4j.util.OrFileFilter; + +import java.io.FileFilter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * It's a {@link PluginManager} that load plugin from a jar file. + * Actually, a plugin is a fat jar, a jar which contains classes from all the libraries, + * on which your project depends and, of course, the classes of current project. + * + * @author Decebal Suiu + */ +public class JarPluginManager extends DefaultPluginManager { + + @Override + protected PluginRepository createPluginRepository() { + return new JarPluginRepository(getPluginsRoot(), isDevelopment()); + } + + @Override + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new JarPluginDescriptorFinder(); + } + + @Override + protected PluginLoader createPluginLoader() { + return new JarPluginLoader(this, pluginClasspath); + } + + class JarPluginRepository extends BasePluginRepository { + + public JarPluginRepository(Path pluginsRoot, boolean development) { + super(pluginsRoot); + + if (development) { + AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); + pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); + setFilter(pluginsFilter); + } else { + setFilter(new JarFileFilter()); + } + } + + protected FileFilter createHiddenPluginFilter(boolean development) { + OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); + + if (development) { + hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); + } + + return hiddenPluginFilter; + } + + } + + class JarPluginDescriptorFinder extends ManifestPluginDescriptorFinder { + + @Override + public Manifest readManifest(Path pluginPath) throws PluginException { + try { + return new JarFile(pluginPath.toFile()).getManifest(); + } catch (IOException e) { + throw new PluginException(e); + } + } + + } + + class JarPluginLoader extends DefaultPluginLoader { + + public JarPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { + super(pluginManager, pluginClasspath); + } + + @Override + public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { + if (isDevelopment()) { + return super.loadPlugin(pluginPath, pluginDescriptor); + } + + PluginClassLoader pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); + pluginClassLoader.addFile(pluginPath.toFile()); + + return pluginClassLoader; + } + + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java index 24ae751..fba63ea 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java @@ -16,14 +16,9 @@ package ro.fortsoft.pf4j; import com.github.zafarkhaja.semver.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import ro.fortsoft.pf4j.util.StringUtils; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; +import java.nio.file.Path; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -32,19 +27,11 @@ import java.util.jar.Manifest; * * @author Decebal Suiu */ -public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { - - private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); - - private PluginClasspath pluginClasspath; - - public ManifestPluginDescriptorFinder(PluginClasspath pluginClasspath) { - this.pluginClasspath = pluginClasspath; - } +public abstract class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { @Override - public PluginDescriptor find(File pluginRepository) throws PluginException { - Manifest manifest = readManifest(pluginRepository); + public PluginDescriptor find(Path pluginPath) throws PluginException { + Manifest manifest = readManifest(pluginPath); PluginDescriptor pluginDescriptor = createPluginDescriptor(manifest); validatePluginDescriptor(pluginDescriptor); @@ -52,37 +39,7 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { return pluginDescriptor; } - protected Manifest readManifest(File pluginRepository) throws PluginException { - // TODO it's ok with first classes directory? Another idea is to specify in PluginClasspath the folder. - String classes = pluginClasspath.getClassesDirectories().get(0); - File manifestFile = new File(pluginRepository, classes + "/META-INF/MANIFEST.MF"); - log.debug("Lookup plugin descriptor in '{}'", manifestFile); - if (!manifestFile.exists()) { - throw new PluginException("Cannot find '" + manifestFile + "' file"); - } - - FileInputStream input = null; - try { - input = new FileInputStream(manifestFile); - } catch (FileNotFoundException e) { - // not happening - } - - Manifest manifest = null; - try { - manifest = new Manifest(input); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } finally { - try { - input.close(); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } - } - - return manifest; - } + public abstract Manifest readManifest(Path pluginPath) throws PluginException; protected PluginDescriptor createPluginDescriptor(Manifest manifest) { PluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java index 093623b..4e31fc3 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java @@ -18,6 +18,7 @@ package ro.fortsoft.pf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; @@ -25,8 +26,8 @@ import java.util.List; /** * One instance of this class should be created by plugin manager for every available plug-in. - * This class loader is a Parent Last ClassLoader - it loads the classes from the plugin's jars before delegating - * to the parent class loader. + * This class loader is a Parent Last ClassLoader - it loads the classes from the plugin's jars + * before delegating to the parent class loader. * * @author Decebal Suiu */ @@ -48,13 +49,23 @@ public class PluginClassLoader extends URLClassLoader { @Override public void addURL(URL url) { + log.debug("Add '{}'", url); super.addURL(url); } + public void addFile(File file) { + try { + addURL(file.getCanonicalFile().toURI().toURL()); + } catch (IOException e) { +// throw new RuntimeException(e); + log.error(e.getMessage(), e); + } + } + /** - * This implementation of loadClass uses a child first delegation model rather than the standard parent first. + * Uses a child first delegation model rather than the standard parent first. * If the requested class cannot be found in this class loader, the parent class loader will be consulted - * via the standard ClassLoader.loadClass(String) mechanism. + * via the standard {@link ClassLoader#loadClass(String)} mechanism. */ @Override public Class loadClass(String className) throws ClassNotFoundException { @@ -67,9 +78,6 @@ public class PluginClassLoader extends URLClassLoader { return getClass().getClassLoader().loadClass(className); } catch (ClassNotFoundException e) { // try next step - // TODO if I uncomment below lines (the correct approach) I received ClassNotFoundException for demo (ro.fortsoft.pf4j.demo) - // log.error(e.getMessage(), e); - // throw e; } } @@ -93,7 +101,7 @@ public class PluginClassLoader extends URLClassLoader { log.trace("Search in dependencies for class '{}'", className); List dependencies = pluginDescriptor.getDependencies(); for (PluginDependency dependency : dependencies) { - PluginClassLoader classLoader = pluginManager.getPluginClassLoader(dependency.getPluginId()); + ClassLoader classLoader = pluginManager.getPluginClassLoader(dependency.getPluginId()); try { return classLoader.loadClass(className); } catch (ClassNotFoundException e) { @@ -103,14 +111,14 @@ public class PluginClassLoader extends URLClassLoader { log.trace("Couldn't find class '{}' in plugin classpath. Delegating to parent", className); - // use the standard URLClassLoader (which follows normal parent delegation) + // use the standard ClassLoader (which follows normal parent delegation) return super.loadClass(className); } } /** - * Load the named resource from this plugin. This implementation checks the plugin's classpath first - * then delegates to the parent. + * Load the named resource from this plugin. + * This implementation checks the plugin's classpath first then delegates to the parent. * * @param name the name of the resource. * @return the URL to the resource, null if the resource was not found. @@ -134,18 +142,4 @@ public class PluginClassLoader extends URLClassLoader { return super.findResource(name); } - /** - * Release all resources acquired by this class loader. - * The current implementation is incomplete. - * For now, this instance can no longer be used to load - * new classes or resources that are defined by this loader. - */ - public void dispose() { - try { - close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java index a49260e..3a6e99d 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java @@ -16,50 +16,39 @@ package ro.fortsoft.pf4j; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** - * The classpath of the plugin after it was unpacked. - * It contains classes directories and lib directories (directories that contains jars). - * All directories are relative to plugin repository. - * The default values are "classes" and "lib". + * The classpath of the plugin. + * It contains {@code classes} directories and {@code lib} directories (directories that contains jars). * * @author Decebal Suiu */ public class PluginClasspath { - private static final String DEFAULT_CLASSES_DIRECTORY = "classes"; - private static final String DEFAULT_LIB_DIRECTORY = "lib"; - - protected List classesDirectories; - protected List libDirectories; + private List classesDirectories; + private List libDirectories; public PluginClasspath() { classesDirectories = new ArrayList<>(); libDirectories = new ArrayList<>(); - - addResources(); } public List getClassesDirectories() { return classesDirectories; } - public void setClassesDirectories(List classesDirectories) { - this.classesDirectories = classesDirectories; + public void addClassesDirectories(String... classesDirectories) { + this.classesDirectories.addAll(Arrays.asList(classesDirectories)); } public List getLibDirectories() { return libDirectories; } - public void setLibDirectories(List libDirectories) { - this.libDirectories = libDirectories; - } - - protected void addResources() { - classesDirectories.add(DEFAULT_CLASSES_DIRECTORY); - libDirectories.add(DEFAULT_LIB_DIRECTORY); + public void addLibDirectories(String... libDirectories) { + this.libDirectories.addAll(Arrays.asList(libDirectories)); } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java index 6cb208c..c84adaf 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java @@ -15,17 +15,17 @@ */ package ro.fortsoft.pf4j; -import java.io.File; +import java.nio.file.Path; /** - * Find a plugin descriptor in a directory (plugin repository). - * You can find in manifest file @see DefaultPluginDescriptorFinder, - * xml file, properties file, java services (with ServiceLoader), etc. + * Find a plugin descriptor for a plugin path. + * You can find in manifest file {@link DefaultPluginDescriptorFinder}, + * xml file, properties file, java services (with {@link java.util.ServiceLoader}), etc. * * @author Decebal Suiu */ public interface PluginDescriptorFinder { - PluginDescriptor find(File pluginRepository) throws PluginException; + PluginDescriptor find(Path pluginPath) throws PluginException; } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java index 9333cb0..ecd0e08 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java @@ -15,120 +15,15 @@ */ package ro.fortsoft.pf4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.DirectoryFileFilter; -import ro.fortsoft.pf4j.util.JarFileFilter; - -import java.io.File; -import java.io.FileFilter; -import java.net.MalformedURLException; -import java.util.List; -import java.util.Vector; +import java.nio.file.Path; /** - * Load all information needed by a plugin. - * This means add all jar files from 'lib' directory, 'classes' to classpath. - * It's a class for only the internal use. + * Load all information (classes) needed by a plugin. * * @author Decebal Suiu */ -class PluginLoader { - - private static final Logger log = LoggerFactory.getLogger(PluginLoader.class); - - /* - * The plugin repository. - */ - private File pluginRepository; - - private PluginClasspath pluginClasspath; - private PluginClassLoader pluginClassLoader; - - public PluginLoader(File pluginRepository, PluginClassLoader pluginClassLoader, PluginClasspath pluginClasspath) { - this.pluginRepository = pluginRepository; - this.pluginClassLoader = pluginClassLoader; - this.pluginClasspath = pluginClasspath; - } - - public boolean load() { - return loadClassesAndJars(); - } - - private boolean loadClassesAndJars() { - return loadClasses() && loadJars(); - } - - private boolean loadClasses() { - List classesDirectories = pluginClasspath.getClassesDirectories(); - - // add each classes directory to plugin class loader - for (String classesDirectory : classesDirectories) { - // make 'classesDirectory' absolute - File file = new File(pluginRepository, classesDirectory).getAbsoluteFile(); - - if (file.exists() && file.isDirectory()) { - log.debug("Found '{}' directory", file.getPath()); - - try { - pluginClassLoader.addURL(file.toURI().toURL()); - log.debug("Added '{}' to the class loader path", file); - } catch (MalformedURLException e) { - e.printStackTrace(); - log.error(e.getMessage(), e); - return false; - } - } - } - - return true; - } - - /** - * Add all *.jar files from lib directories to class loader. - */ - private boolean loadJars() { - List libDirectories = pluginClasspath.getLibDirectories(); - - // add each jars directory to plugin class loader - for (String libDirectory : libDirectories) { - // make 'libDirectory' absolute - File file = new File(pluginRepository, libDirectory).getAbsoluteFile(); - - // collect all jars from current lib directory in jars variable - Vector jars = new Vector<>(); - getJars(jars, file); - for (File jar : jars) { - try { - pluginClassLoader.addURL(jar.toURI().toURL()); - log.debug("Added '{}' to the class loader path", jar); - } catch (MalformedURLException e) { - e.printStackTrace(); - log.error(e.getMessage(), e); - return false; - } - } - } - - return true; - } - - private void getJars(Vector bucket, File file) { - FileFilter jarFilter = new JarFileFilter(); - FileFilter directoryFilter = new DirectoryFileFilter(); - - if (file.exists() && file.isDirectory() && file.isAbsolute()) { - File[] jars = file.listFiles(jarFilter); - for (int i = 0; (jars != null) && (i < jars.length); ++i) { - bucket.addElement(jars[i]); - } +public interface PluginLoader { - File[] directories = file.listFiles(directoryFilter); - for (int i = 0; (directories != null) && (i < directories.length); ++i) { - File directory = directories[i]; - getJars(bucket, directory); - } - } - } + ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor); } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java index 5f23e5c..f023e29 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java @@ -16,7 +16,8 @@ package ro.fortsoft.pf4j; import com.github.zafarkhaja.semver.Version; -import java.io.File; + +import java.nio.file.Path; import java.util.List; import java.util.Set; @@ -69,10 +70,10 @@ public interface PluginManager { /** * Load a plugin. * - * @param pluginArchiveFile + * @param pluginPath * @return the pluginId of the installed plugin or null */ - String loadPlugin(File pluginArchiveFile); + String loadPlugin(Path pluginPath); /** * Start all active plugins. @@ -130,7 +131,7 @@ public interface PluginManager { */ boolean deletePlugin(String pluginId); - PluginClassLoader getPluginClassLoader(String pluginId); + ClassLoader getPluginClassLoader(String pluginId); List getExtensions(Class type); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java index 5db96b5..6f57c57 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java @@ -15,11 +15,11 @@ */ package ro.fortsoft.pf4j; -import java.io.File; +import java.nio.file.Path; import java.util.List; /** - * Directory whose contents are .zip files used as plugins. + * Directory that contains plugins. A plugin could be a zip file. * * @author Decebal Suiu * @author Mário Franco @@ -27,11 +27,11 @@ import java.util.List; public interface PluginRepository { /** - * List all plugin archive filed. + * List all plugin paths. * * @return a list of files */ - List getPluginArchives(); + List getPluginPaths(); /** * Removes a plugin from the repository. @@ -39,6 +39,6 @@ public interface PluginRepository { * @param pluginPath the plugin path * @return true if deleted */ - boolean deletePluginArchive(String pluginPath); + boolean deletePluginPath(Path pluginPath); } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java index 468d30b..e594f68 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java @@ -15,6 +15,8 @@ */ package ro.fortsoft.pf4j; +import java.nio.file.Path; + /** * A wrapper over plugin instance. * @@ -22,16 +24,17 @@ package ro.fortsoft.pf4j; */ public class PluginWrapper { - PluginManager pluginManager; - PluginDescriptor descriptor; - String pluginPath; - PluginClassLoader pluginClassLoader; - PluginFactory pluginFactory; - PluginState pluginState; - RuntimeMode runtimeMode; + private PluginManager pluginManager; + private PluginDescriptor descriptor; + private Path pluginPath; + private ClassLoader pluginClassLoader; + private PluginFactory pluginFactory; + private PluginState pluginState; + private RuntimeMode runtimeMode; + Plugin plugin; // cache - public PluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor, String pluginPath, PluginClassLoader pluginClassLoader) { + public PluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor, Path pluginPath, ClassLoader pluginClassLoader) { this.pluginManager = pluginManager; this.descriptor = descriptor; this.pluginPath = pluginPath; @@ -55,9 +58,9 @@ public class PluginWrapper { } /** - * Returns the path of this plugin relative to plugins directory. + * Returns the path of this plugin. */ - public String getPluginPath() { + public Path getPluginPath() { return pluginPath; } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java index 544d76e..b8595bb 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java @@ -20,11 +20,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.fortsoft.pf4j.util.StringUtils; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Properties; /** @@ -49,8 +49,8 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder } @Override - public PluginDescriptor find(File pluginRepository) throws PluginException { - Properties properties = readProperties(pluginRepository); + public PluginDescriptor find(Path pluginPath) throws PluginException { + Properties properties = readProperties(pluginPath); PluginDescriptor pluginDescriptor = createPluginDescriptor(properties); validatePluginDescriptor(pluginDescriptor); @@ -58,31 +58,18 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder return pluginDescriptor; } - protected Properties readProperties(File pluginRepository) throws PluginException { - File propertiesFile = new File(pluginRepository, propertiesFileName); - log.debug("Lookup plugin descriptor in '{}'", propertiesFile); - if (!propertiesFile.exists()) { - throw new PluginException("Cannot find '" + propertiesFile + "' file"); - } - - InputStream input = null; - try { - input = new FileInputStream(propertiesFile); - } catch (FileNotFoundException e) { - // not happening + protected Properties readProperties(Path pluginPath) throws PluginException { + Path propertiesPath = pluginPath.resolve(Paths.get(propertiesFileName)); + log.debug("Lookup plugin descriptor in '{}'", propertiesPath); + if (Files.notExists(propertiesPath)) { + throw new PluginException("Cannot find '" + pluginPath + "' path"); } Properties properties = new Properties(); - try { + try (InputStream input = Files.newInputStream(propertiesPath)) { properties.load(input); } catch (IOException e) { throw new PluginException(e.getMessage(), e); - } finally { - try { - input.close(); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } } return properties; diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java index d394219..9e2ca10 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java @@ -18,6 +18,7 @@ package ro.fortsoft.pf4j.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; +import java.io.FileFilter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -95,4 +96,29 @@ public class FileUtils { return success; } + public static List getJars(File folder) { + List bucket = new ArrayList<>(); + getJars(bucket, folder); + + return bucket; + } + + private static void getJars(List bucket, File folder) { + FileFilter jarFilter = new JarFileFilter(); + FileFilter directoryFilter = new DirectoryFileFilter(); + + if (folder.exists() && folder.isDirectory() && folder.isAbsolute()) { + File[] jars = folder.listFiles(jarFilter); + for (int i = 0; (jars != null) && (i < jars.length); ++i) { + bucket.add(jars[i]); + } + + File[] directories = folder.listFiles(directoryFilter); + for (int i = 0; (directories != null) && (i < directories.length); ++i) { + File directory = directories[i]; + getJars(bucket, directory); + } + } + } + } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java index abbc712..fc9f6d0 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java @@ -27,7 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * This class extracts the containt of the plugin archive into a directory. + * This class extracts the content of the plugin zip into a directory. * It's a class for only the internal use. * * @author Decebal Suiu @@ -70,8 +70,8 @@ public class Unzip { removeDirectory(destination); ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source)); - ZipEntry zipEntry = null; + ZipEntry zipEntry; while ((zipEntry = zipInputStream.getNextEntry()) != null) { try { File file = new File(destination, zipEntry.getName()); diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java index 7f51d6f..2b1dc32 100644 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java +++ b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java @@ -19,10 +19,9 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import ro.fortsoft.pf4j.util.ZipFileFilter; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.List; import static org.junit.Assert.assertEquals; @@ -31,6 +30,7 @@ import static org.junit.Assert.assertTrue; /** * @author Mario Franco + * @author Decebal Suiu */ public class DefaultPluginRepositoryTest { @@ -39,52 +39,53 @@ public class DefaultPluginRepositoryTest { @Before public void setUp() throws IOException { - testFolder.newFile("plugin-1.zip"); - testFolder.newFile("plugin-2.zip"); - testFolder.newFile("plugin-3.zi_"); + testFolder.newFolder("plugin-1"); + testFolder.newFolder("plugin-2"); + testFolder.newFolder("plugin-3"); } /** - * Test of getPluginArchives method, of class DefaultPluginRepository. + * Test of {@link DefaultPluginRepository#getPluginPaths()} method. */ @Test public void testGetPluginArchives() { - DefaultPluginRepository instance = new DefaultPluginRepository(testFolder.getRoot(), new ZipFileFilter()); + Path pluginsRoot = getPluginsRoot(); - List result = instance.getPluginArchives(); + PluginRepository instance = new DefaultPluginRepository(pluginsRoot, false); - assertEquals(2, result.size()); - assertFileExists(result, "plugin-1.zip"); - assertFileExists(result, "plugin-2.zip"); + List result = instance.getPluginPaths(); + + assertEquals(3, result.size()); + assertPathExists(result, pluginsRoot.resolve("plugin-1")); + assertPathExists(result, pluginsRoot.resolve("plugin-2")); + assertPathExists(result, pluginsRoot.resolve("plugin-3")); } /** - * Test of deletePluginArchive method, of class DefaultPluginRepository. + * Test of {@link DefaultPluginRepository#deletePluginPath(Path)} method. */ @Test - public void testDeletePluginArchive() { - DefaultPluginRepository instance = new DefaultPluginRepository(testFolder.getRoot(), new ZipFileFilter()); + public void testDeletePluginPath() { + Path pluginsRoot = getPluginsRoot(); + + PluginRepository instance = new DefaultPluginRepository(pluginsRoot, false); - assertTrue(instance.deletePluginArchive("/plugin-1")); - assertFalse(instance.deletePluginArchive("/plugin-3")); + assertTrue(instance.deletePluginPath(pluginsRoot.resolve("plugin-1"))); + assertTrue(instance.deletePluginPath(pluginsRoot.resolve("plugin-3"))); + assertFalse(instance.deletePluginPath(pluginsRoot.resolve("plugin-4"))); - List result = instance.getPluginArchives(); + List result = instance.getPluginPaths(); assertEquals(1, result.size()); - assertEquals(result.get(0).getName(), "plugin-2.zip"); + assertEquals(pluginsRoot.relativize(result.get(0)).toString(), "plugin-2"); } - public static void assertFileExists(List files, String file) { - boolean found = false; - - for (File f : files) { - if (f.getName().equals(file)) { - found = true; - break; - } - } + private void assertPathExists(List paths, Path path) { + assertTrue("The directory must contains the file " + path, paths.contains(path)); + } - assertTrue("The directory must contains the file " + file, found); + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); } } diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java index 25dc7a1..db33d65 100644 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java +++ b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java @@ -22,13 +22,16 @@ import ro.fortsoft.pf4j.util.FileUtils; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * @author Mario Franco + * @author Decebal Suiu */ public class DefaultPluginStatusProviderTest { @@ -42,7 +45,8 @@ public class DefaultPluginStatusProviderTest { public void testIsPluginDisabled() throws IOException { createEnabledFile(); createDisabledFile(); - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertFalse(instance.isPluginDisabled("plugin-1")); assertTrue(instance.isPluginDisabled("plugin-2")); @@ -55,7 +59,8 @@ public class DefaultPluginStatusProviderTest { @Test public void testIsPluginDisabledWithEnableEmpty() throws IOException { createDisabledFile(); - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertFalse(instance.isPluginDisabled("plugin-1")); assertTrue(instance.isPluginDisabled("plugin-2")); @@ -69,7 +74,8 @@ public class DefaultPluginStatusProviderTest { public void testDisablePlugin() throws IOException { createEnabledFile(); createDisabledFile(); - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertTrue(instance.disablePlugin("plugin-1")); assertTrue(instance.isPluginDisabled("plugin-1")); @@ -83,7 +89,8 @@ public class DefaultPluginStatusProviderTest { @Test public void testDisablePluginWithEnableEmpty() throws IOException { createDisabledFile(); - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertTrue(instance.disablePlugin("plugin-1")); assertTrue(instance.isPluginDisabled("plugin-1")); @@ -97,7 +104,8 @@ public class DefaultPluginStatusProviderTest { @Test public void testEnablePlugin() throws IOException { createEnabledFile(); - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertTrue(instance.enablePlugin("plugin-2")); assertFalse(instance.isPluginDisabled("plugin-1")); @@ -110,7 +118,7 @@ public class DefaultPluginStatusProviderTest { */ @Test public void testEnablePluginWithEnableEmpty() { - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertTrue(instance.enablePlugin("plugin-2")); assertFalse(instance.isPluginDisabled("plugin-1")); @@ -123,7 +131,7 @@ public class DefaultPluginStatusProviderTest { */ @Test public void testDisablePluginWithoutDisabledFile() throws IOException { - DefaultPluginStatusProvider instance = new DefaultPluginStatusProvider(testFolder.getRoot()); + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); assertFalse(instance.isPluginDisabled("plugin-1")); assertTrue(instance.disablePlugin("plugin-1")); @@ -150,4 +158,8 @@ public class DefaultPluginStatusProviderTest { FileUtils.writeLines(lines, file); } + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + } diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java index cffbc04..0e94fcd 100644 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java +++ b/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java @@ -21,18 +21,19 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * @author Mario Franco + * @author Decebal Suiu */ public class ManifestPluginDescriptorFinderTest { @@ -43,43 +44,42 @@ public class ManifestPluginDescriptorFinderTest { public void setUp() throws IOException { Charset charset = Charset.forName("UTF-8"); - File plugin = testFolder.newFolder("test-plugin-1", "classes", "META-INF"); - Files.write(Paths.get(plugin.getPath(), "extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(Paths.get(plugin.getPath(), "MANIFEST.MF"), getPlugin1Manifest(), charset); + Path pluginPath = testFolder.newFolder("test-plugin-1", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin1Manifest(), charset); - plugin = testFolder.newFolder("test-plugin-2", "classes", "META-INF"); - Files.write(Paths.get(plugin.getPath(), "extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(Paths.get(plugin.getPath(), "MANIFEST.MF"), getPlugin2Manifest(), charset); + pluginPath = testFolder.newFolder("test-plugin-2", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin2Manifest(), charset); - // Empty Plugin + // empty plugin testFolder.newFolder("test-plugin-3"); - // No Plugin Class - plugin = testFolder.newFolder("test-plugin-4", "classes", "META-INF"); - Files.write(Paths.get(plugin.getPath(), "extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(Paths.get(plugin.getPath(), "MANIFEST.MF"), getPlugin4Manifest(), charset); + // no plugin class + pluginPath = testFolder.newFolder("test-plugin-4", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin4Manifest(), charset); - // No Plugin Version - plugin = testFolder.newFolder("test-plugin-5", "classes", "META-INF"); - Files.write(Paths.get(plugin.getPath(), "extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(Paths.get(plugin.getPath(), "MANIFEST.MF"), getPlugin5Manifest(), charset); + // no plugin version + pluginPath = testFolder.newFolder("test-plugin-5", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin5Manifest(), charset); - // No Plugin Id - plugin = testFolder.newFolder("test-plugin-6", "classes", "META-INF"); - Files.write(Paths.get(plugin.getPath(), "extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(Paths.get(plugin.getPath(), "MANIFEST.MF"), getPlugin6Manifest(), charset); + // no plugin id + pluginPath = testFolder.newFolder("test-plugin-6", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin6Manifest(), charset); } /** - * Test of find method, of class ManifestPluginDescriptorFinder. + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. */ @Test public void testFind() throws Exception { - DefaultPluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new PluginClasspath()); + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); - PluginDescriptor plugin1 = instance.find(Paths.get(testFolder.getRoot().getPath(),"test-plugin-1").toFile()); - - PluginDescriptor plugin2 = instance.find(Paths.get(testFolder.getRoot().getPath(),"test-plugin-2").toFile()); + PluginDescriptor plugin1 = instance.find(getPluginsRoot().resolve("test-plugin-1")); + PluginDescriptor plugin2 = instance.find(getPluginsRoot().resolve("test-plugin-2")); assertEquals("test-plugin-1", plugin1.getPluginId()); assertEquals("Test Plugin 1", plugin1.getPluginDescription()); @@ -102,48 +102,44 @@ public class ManifestPluginDescriptorFinderTest { } /** - * Test of find method, of class ManifestPluginDescriptorFinder. + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. */ @Test(expected = PluginException.class) public void testFindNotFound() throws Exception { - - ManifestPluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new PluginClasspath()); - PluginDescriptor result = instance.find(Paths.get(testFolder.getRoot().getPath(),"test-plugin-3").toFile()); + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + instance.find(getPluginsRoot().resolve("test-plugin-3")); } /** - * Test of find method, of class ManifestPluginDescriptorFinder. + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. */ @Test(expected = PluginException.class) public void testFindMissingPluginClass() throws Exception { - - ManifestPluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new PluginClasspath()); - PluginDescriptor result = instance.find(Paths.get(testFolder.getRoot().getPath(),"test-plugin-4").toFile()); + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + instance.find(getPluginsRoot().resolve("test-plugin-4")); } /** - * Test of find method, of class ManifestPluginDescriptorFinder. + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. */ @Test(expected = PluginException.class) public void testFindMissingPluginVersion() throws Exception { - - ManifestPluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new PluginClasspath()); - PluginDescriptor result = instance.find(Paths.get(testFolder.getRoot().getPath(),"test-plugin-5").toFile()); + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + instance.find(getPluginsRoot().resolve("test-plugin-5")); } /** - * Test of find method, of class ManifestPluginDescriptorFinder. + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. */ @Test(expected = PluginException.class) public void testFindMissingPluginId() throws Exception { - - ManifestPluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new PluginClasspath()); - PluginDescriptor result = instance.find(Paths.get(testFolder.getRoot().getPath(),"test-plugin-6").toFile()); + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + instance.find(getPluginsRoot().resolve("test-plugin-6")); } private List getPlugin1Manifest() { - - String[] lines = new String[]{"Manifest-Version: 1.0\n" + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + "Implementation-Title: Test Plugin #1\n" + "Implementation-Version: 0.10.0-SNAPSHOT\n" + "Archiver-Version: Plexus Archiver\n" @@ -161,14 +157,15 @@ public class ManifestPluginDescriptorFinderTest { + "Build-Jdk: 1.8.0_45\n" + "Specification-Version: 0.10.0-SNAPSHOT\n" + "\n" - + ""}; + + "" + }; return Arrays.asList(lines); } private List getPlugin2Manifest() { - - String[] lines = new String[]{"Manifest-Version: 1.0\n" + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + "Plugin-Dependencies: \n" + "Implementation-Title: Test Plugin #2\n" + "Implementation-Version: 0.10.0-SNAPSHOT\n" @@ -184,14 +181,15 @@ public class ManifestPluginDescriptorFinderTest { + "Build-Jdk: 1.8.0_45\n" + "Specification-Version: 0.10.0-SNAPSHOT\n" + "\n" - + ""}; + + "" + }; return Arrays.asList(lines); } private List getPlugin4Manifest() { - - String[] lines = new String[]{"Manifest-Version: 1.0\n" + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + "Implementation-Title: Test Plugin #4\n" + "Implementation-Version: 0.10.0-SNAPSHOT\n" + "Archiver-Version: Plexus Archiver\n" @@ -205,14 +203,15 @@ public class ManifestPluginDescriptorFinderTest { + "Build-Jdk: 1.8.0_45\n" + "Specification-Version: 0.10.0-SNAPSHOT\n" + "\n" - + ""}; + + "" + }; return Arrays.asList(lines); } private List getPlugin5Manifest() { - - String[] lines = new String[]{"Manifest-Version: 1.0\n" + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + "Implementation-Title: Test Plugin #5\n" + "Implementation-Version: 0.10.0-SNAPSHOT\n" + "Archiver-Version: Plexus Archiver\n" @@ -226,14 +225,15 @@ public class ManifestPluginDescriptorFinderTest { + "Build-Jdk: 1.8.0_45\n" + "Specification-Version: 0.10.0-SNAPSHOT\n" + "\n" - + ""}; + + "" + }; return Arrays.asList(lines); } private List getPlugin6Manifest() { - - String[] lines = new String[]{"Manifest-Version: 1.0\n" + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + "Implementation-Title: Test Plugin #6\n" + "Implementation-Version: 0.10.0-SNAPSHOT\n" + "Archiver-Version: Plexus Archiver\n" @@ -246,8 +246,14 @@ public class ManifestPluginDescriptorFinderTest { + "Build-Jdk: 1.8.0_45\n" + "Specification-Version: 0.10.0-SNAPSHOT\n" + "\n" - + ""}; + + "" + }; return Arrays.asList(lines); } + + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + } diff --git a/pf4j/src/test/resources/log4j.properties b/pf4j/src/test/resources/log4j.properties new file mode 100644 index 0000000..0454ba2 --- /dev/null +++ b/pf4j/src/test/resources/log4j.properties @@ -0,0 +1,20 @@ +log4j.rootLogger=DEBUG, Console + +# +# PF4J log +# +log4j.logger.ro.fortsoft.pf4j=DEBUG, Console +# !!! Put the bellow classes on level TRACE when you are in trouble +log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=WARN, Console +log4j.logger.ro.fortsoft.pf4j.AbstractExtensionFinder=DEBUG, Console +log4j.additivity.ro.fortsoft.pf4j=false +log4j.additivity.ro.fortsoft.pf4j.PluginClassLoader=false +log4j.additivity.ro.fortsoft.pf4j.AbstractExtensionFinder=false + +# +# Appenders +# +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.layout=org.apache.log4j.PatternLayout +#log4j.appender.Console.layout.conversionPattern=%-5p - %-32.32c{1} - %m\n +log4j.appender.Console.layout.ConversionPattern=%d %p %c - %m%n