diff options
author | Decebal Suiu <decebal.suiu@gmail.com> | 2013-10-01 09:23:48 +0300 |
---|---|---|
committer | Decebal Suiu <decebal.suiu@gmail.com> | 2013-10-01 09:23:48 +0300 |
commit | c18773b3ed9735f475844e2abf2a253412c46fc0 (patch) | |
tree | 5f3e6617b20f31948c657e1c1e2adb8b060361eb | |
parent | 60fb88af769ad4f43ca35bc5527d57c982dcaaa2 (diff) | |
parent | aab4e0129a722f98a70ca1b9ed1917befd31c9f0 (diff) | |
download | pf4j-c18773b3ed9735f475844e2abf2a253412c46fc0.tar.gz pf4j-c18773b3ed9735f475844e2abf2a253412c46fc0.zip |
Merge branch 'master' of https://github.com/decebals/pf4j
36 files changed, 713 insertions, 306 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00a7233 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +target/ +.classpath +.project +.settings + diff --git a/demo/api/pom.xml b/demo/api/pom.xml index 2a2a04b..1818ec5 100644 --- a/demo/api/pom.xml +++ b/demo/api/pom.xml @@ -4,12 +4,12 @@ <parent> <groupId>ro.fortsoft.pf4j.demo</groupId> <artifactId>pf4j-demo-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>pf4j-demo-api</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>jar</packaging> <name>Demo Api</name> diff --git a/demo/app/pom.xml b/demo/app/pom.xml index cef9586..bf07815 100644 --- a/demo/app/pom.xml +++ b/demo/app/pom.xml @@ -4,12 +4,12 @@ <parent> <groupId>ro.fortsoft.pf4j.demo</groupId> <artifactId>pf4j-demo-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>pf4j-demo-app</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>jar</packaging> <name>Demo App</name> 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 9f218cf..7681a75 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 @@ -12,12 +12,19 @@ */ package ro.fortsoft.pf4j.demo; +import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; import org.apache.commons.lang.StringUtils; import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.PluginClasspath; +import ro.fortsoft.pf4j.PluginDescriptorFinder; import ro.fortsoft.pf4j.PluginManager; +import ro.fortsoft.pf4j.PropertiesPluginDescriptorFinder; import ro.fortsoft.pf4j.demo.api.Greeting; /** @@ -26,21 +33,25 @@ import ro.fortsoft.pf4j.demo.api.Greeting; * @author Decebal Suiu */ public class Boot { - + public static void main(String[] args) { // print logo printLogo(); - // load and start (active/resolved) plugins - final PluginManager pluginManager = new DefaultPluginManager(); + // create the plugin manager + final PluginManager pluginManager = createPluginManager(); + + // load and start (active/resolved) the plugins pluginManager.loadPlugins(); pluginManager.startPlugins(); + // retrieves the extensions for Greeting extension point List<Greeting> greetings = pluginManager.getExtensions(Greeting.class); for (Greeting greeting : greetings) { System.out.println(">>> " + greeting.getGreeting()); } + // stop the plugins pluginManager.stopPlugins(); /* Runtime.getRuntime().addShutdownHook(new Thread() { @@ -53,11 +64,82 @@ public class Boot { }); */ } + + private static PluginManager createPluginManager() { + // retrieves the pf4j runtime mode + String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.PROD.toString()); + RuntimeMode mode = RuntimeMode.byName(modeAsString); + + System.out.println("PF4J runtime mode: '" + mode + "'"); + + // create the plugin manager depending on runtime mode + PluginManager pluginManager = null; + if (mode == RuntimeMode.PROD) { + pluginManager = new DefaultPluginManager(); + } else if (mode == RuntimeMode.DEV) { + // run from eclipse IDE (for example) + pluginManager = new DefaultPluginManager(new File("../plugins")) { + + @Override + protected PluginClasspath createPluginClasspath() { + PluginClasspath pluginClasspath = super.createPluginClasspath(); + // modify plugin classes + List<String> pluginClasses = pluginClasspath.getClassesDirectories(); + pluginClasses.clear(); + pluginClasses.add("target/classes"); + + return pluginClasspath; + } + + @Override + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return new PropertiesPluginDescriptorFinder(); + } + + }; + } + + return pluginManager; + } private static void printLogo() { System.out.println(StringUtils.repeat("#", 40)); System.out.println(StringUtils.center("PF4J-DEMO", 40)); System.out.println(StringUtils.repeat("#", 40)); } + + public enum RuntimeMode { + + DEV("dev"), // development + PROD("prod"); // production + + private final String name; + + private static final Map<String, RuntimeMode> map = new HashMap<String, RuntimeMode>(); + + static { + for (RuntimeMode mode : RuntimeMode.values()) { + map.put(mode.name, mode); + } + } + + private RuntimeMode(final String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public static RuntimeMode byName(String name) { + if (map.containsKey(name)) { + return map.get(name); + } + + throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name + "'"); + } + + } } diff --git a/demo/app/src/main/resources/log4j.properties b/demo/app/src/main/resources/log4j.properties index f2160e8..3ac64fd 100644 --- a/demo/app/src/main/resources/log4j.properties +++ b/demo/app/src/main/resources/log4j.properties @@ -2,5 +2,5 @@ log4j.rootLogger=DEBUG,Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout -log4j.appender.Console.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n +log4j.appender.Console.layout.conversionPattern=%-5p - %-32.32c{1} - %m\n diff --git a/demo/plugin1/plugin.properties b/demo/plugins/plugin1/plugin.properties index 4f95d99..4f95d99 100644 --- a/demo/plugin1/plugin.properties +++ b/demo/plugins/plugin1/plugin.properties diff --git a/demo/plugin1/pom.xml b/demo/plugins/plugin1/pom.xml index 49cf1df..8fe5f7b 100644 --- a/demo/plugin1/pom.xml +++ b/demo/plugins/plugin1/pom.xml @@ -3,13 +3,13 @@ <parent> <groupId>ro.fortsoft.pf4j.demo</groupId> - <artifactId>pf4j-demo-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <artifactId>pf4j-demo-plugins</artifactId> + <version>0.6-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>pf4j-demo-plugin1</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>jar</packaging> <name>Demo Plugin #1</name> diff --git a/demo/plugin1/src/main/assembly/assembly.xml b/demo/plugins/plugin1/src/main/assembly/assembly.xml index 3fdc464..3fdc464 100644 --- a/demo/plugin1/src/main/assembly/assembly.xml +++ b/demo/plugins/plugin1/src/main/assembly/assembly.xml diff --git a/demo/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java b/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java index d10d3dd..d10d3dd 100644 --- a/demo/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java +++ b/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java diff --git a/demo/plugin2/plugin.properties b/demo/plugins/plugin2/plugin.properties index 0de45e6..0de45e6 100644 --- a/demo/plugin2/plugin.properties +++ b/demo/plugins/plugin2/plugin.properties diff --git a/demo/plugin2/pom.xml b/demo/plugins/plugin2/pom.xml index b69f767..2bf0dd8 100644 --- a/demo/plugin2/pom.xml +++ b/demo/plugins/plugin2/pom.xml @@ -3,13 +3,13 @@ <parent> <groupId>ro.fortsoft.pf4j.demo</groupId> - <artifactId>pf4j-demo-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <artifactId>pf4j-demo-plugins</artifactId> + <version>0.6-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>pf4j-demo-plugin2</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>jar</packaging> <name>Demo Plugin #2</name> diff --git a/demo/plugin2/src/main/assembly/assembly.xml b/demo/plugins/plugin2/src/main/assembly/assembly.xml index 5cefe0d..5cefe0d 100644 --- a/demo/plugin2/src/main/assembly/assembly.xml +++ b/demo/plugins/plugin2/src/main/assembly/assembly.xml diff --git a/demo/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java b/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java index 8f12e23..8f12e23 100644 --- a/demo/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java +++ b/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java diff --git a/demo/plugins/pom.xml b/demo/plugins/pom.xml new file mode 100644 index 0000000..d451b31 --- /dev/null +++ b/demo/plugins/pom.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>ro.fortsoft.pf4j.demo</groupId> + <artifactId>pf4j-demo-parent</artifactId> + <version>0.6-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>ro.fortsoft.pf4j.demo</groupId> + <artifactId>pf4j-demo-plugins</artifactId> + <version>0.6-SNAPSHOT</version> + <packaging>pom</packaging> + <name>Demo Plugins Parent</name> + + <build> + <resources> + <resource> + <filtering>false</filtering> + <directory>src/main/java</directory> + <excludes> + <exclude>**/*.java</exclude> + </excludes> + </resource> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + </build> + + <modules> + <module>plugin1</module> + <module>plugin2</module> + </modules> + +</project> diff --git a/demo/pom.xml b/demo/pom.xml index 76bd4d7..7d00cd7 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -4,13 +4,13 @@ <parent> <groupId>ro.fortsoft.pf4j</groupId> <artifactId>pf4j-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>ro.fortsoft.pf4j.demo</groupId> <artifactId>pf4j-demo-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>pom</packaging> <name>Demo Parent</name> @@ -32,8 +32,7 @@ <modules> <module>app</module> <module>api</module> - <module>plugin1</module> - <module>plugin2</module> + <module>plugins</module> </modules> </project> diff --git a/pf4j/pom.xml b/pf4j/pom.xml index 68b0710..0859a55 100644 --- a/pf4j/pom.xml +++ b/pf4j/pom.xml @@ -4,12 +4,12 @@ <parent> <groupId>ro.fortsoft.pf4j</groupId> <artifactId>pf4j-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>pf4j</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>jar</packaging> <name>PF4J</name> <description>Plugin Framework for Java</description> diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java index 492a701..66c1936 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Decebal Suiu + * Copyright 2013 Decebal Suiu * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: @@ -12,69 +12,16 @@ */ package ro.fortsoft.pf4j; -import java.lang.reflect.AnnotatedElement; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.java.sezpoz.Index; -import net.java.sezpoz.IndexItem; - /** - * Using Sezpoz(http://sezpoz.java.net/) for extensions discovery. + * The default implementation for ExtensionFinder. + * Now, this class it's a "link" to {@link ro.fortsoft.pf4j.SezpozExtensionFinder}. * * @author Decebal Suiu */ -public class DefaultExtensionFinder implements ExtensionFinder { +public class DefaultExtensionFinder extends SezpozExtensionFinder { - private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFinder.class); - - private volatile List<IndexItem<Extension, Object>> indices; - private ClassLoader classLoader; - public DefaultExtensionFinder(ClassLoader classLoader) { - this.classLoader = classLoader; + super(classLoader); } - @Override - public <T> List<ExtensionWrapper<T>> find(Class<T> type) { - log.debug("Find extensions for " + type); - List<ExtensionWrapper<T>> result = new ArrayList<ExtensionWrapper<T>>(); - getIndices(); -// System.out.println("indices = "+ indices); - for (IndexItem<Extension, Object> item : indices) { - try { - AnnotatedElement element = item.element(); - Class<?> extensionType = (Class<?>) element; - log.debug("Checking extension type " + extensionType); - if (type.isAssignableFrom(extensionType)) { - Object instance = item.instance(); - if (instance != null) { - log.debug("Added extension " + extensionType); - result.add(new ExtensionWrapper<T>(type.cast(instance), item.annotation().ordinal())); - } - } - } catch (InstantiationException e) { - log.error(e.getMessage(), e); - } - } - - return result; - } - - private List<IndexItem<Extension, Object>> getIndices() { - if (indices == null) { - indices = new ArrayList<IndexItem<Extension, Object>>(); - Iterator<IndexItem<Extension, Object>> it = Index.load(Extension.class, Object.class, classLoader).iterator(); - while (it.hasNext()) { - indices.add(it.next()); - } - } - - return indices; - } - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java index 3e23ff1..144fc6e 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Decebal Suiu + * Copyright 2013 Decebal Suiu * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: @@ -12,78 +12,16 @@ */ package ro.fortsoft.pf4j; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -import ro.fortsoft.pf4j.util.StringUtils; - /** - * Read the plugin descriptor from the manifest file. - * + * The default implementation for PluginDescriptorFinder. + * Now, this class it's a "link" to {@link ro.fortsoft.pf4j.ManifestPluginDescriptorFinder}. + * * @author Decebal Suiu */ -public class DefaultPluginDescriptorFinder implements PluginDescriptorFinder { - - @Override - public PluginDescriptor find(File pluginRepository) throws PluginException { - // TODO it's ok with classes/ ? - File manifestFile = new File(pluginRepository, "classes/META-INF/MANIFEST.MF"); - 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); - } - } - - PluginDescriptor pluginDescriptor = new PluginDescriptor(); - - // TODO validate !!! - Attributes attrs = manifest.getMainAttributes(); - String id = attrs.getValue("Plugin-Id"); - if (StringUtils.isEmpty(id)) { - throw new PluginException("Plugin-Id cannot be empty"); - } - pluginDescriptor.setPluginId(id); - - String clazz = attrs.getValue("Plugin-Class"); - if (StringUtils.isEmpty(clazz)) { - throw new PluginException("Plugin-Class cannot be empty"); - } - pluginDescriptor.setPluginClass(clazz); - - String version = attrs.getValue("Plugin-Version"); - if (StringUtils.isEmpty(version)) { - throw new PluginException("Plugin-Version cannot be empty"); - } - pluginDescriptor.setPluginVersion(PluginVersion.createVersion(version)); - - String provider = attrs.getValue("Plugin-Provider"); - pluginDescriptor.setProvider(provider); - String dependencies = attrs.getValue("Plugin-Dependencies"); - pluginDescriptor.setDependencies(dependencies); +public class DefaultPluginDescriptorFinder extends ManifestPluginDescriptorFinder { - return pluginDescriptor; + public DefaultPluginDescriptorFinder(PluginClasspath pluginClasspath) { + super(pluginClasspath); } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index 20aba2d..0c2628d 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -13,7 +13,7 @@ package ro.fortsoft.pf4j; import java.io.File; -import java.io.FilenameFilter; +import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -24,11 +24,14 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ro.fortsoft.pf4j.util.AndFileFilter; import ro.fortsoft.pf4j.util.CompoundClassLoader; -import ro.fortsoft.pf4j.util.DirectoryFilter; +import ro.fortsoft.pf4j.util.DirectoryFileFilter; import ro.fortsoft.pf4j.util.FileUtils; +import ro.fortsoft.pf4j.util.HiddenFilter; +import ro.fortsoft.pf4j.util.NotFileFilter; import ro.fortsoft.pf4j.util.Unzip; -import ro.fortsoft.pf4j.util.ZipFilter; +import ro.fortsoft.pf4j.util.ZipFileFilter; /** * Default implementation of the PluginManager interface. @@ -44,9 +47,11 @@ public class DefaultPluginManager implements PluginManager { */ private File pluginsDirectory; - private ExtensionFinder extensionFinder; + private final ExtensionFinder extensionFinder; - private PluginDescriptorFinder pluginDescriptorFinder; + private final PluginDescriptorFinder pluginDescriptorFinder; + + private final PluginClasspath pluginClasspath; /** * A map of plugins this manager is responsible for (the key is the 'pluginId'). @@ -111,17 +116,18 @@ public class DefaultPluginManager implements PluginManager { disabledPlugins = new ArrayList<String>(); compoundClassLoader = new CompoundClassLoader(); + pluginClasspath = createPluginClasspath(); pluginDescriptorFinder = createPluginDescriptorFinder(); extensionFinder = createExtensionFinder(); try { // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file) enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true); - log.info("Enabled plugins: " + enabledPlugins); + log.info("Enabled plugins: {}", enabledPlugins); // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file) disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true); - log.info("Disabled plugins: " + disabledPlugins); + log.info("Disabled plugins: {}", disabledPlugins); } catch (IOException e) { log.error(e.getMessage(), e); } @@ -160,7 +166,7 @@ public class DefaultPluginManager implements PluginManager { public void startPlugins() { for (PluginWrapper pluginWrapper : resolvedPlugins) { try { - log.info("Start plugin '" + pluginWrapper.getDescriptor().getPluginId() + "'"); + log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); pluginWrapper.getPlugin().start(); pluginWrapper.setPluginState(PluginState.STARTED); startedPlugins.add(pluginWrapper); @@ -179,7 +185,7 @@ public class DefaultPluginManager implements PluginManager { Collections.reverse(startedPlugins); for (PluginWrapper pluginWrapper : startedPlugins) { try { - log.info("Stop plugin '" + pluginWrapper.getDescriptor().getPluginId() + "'"); + log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); pluginWrapper.getPlugin().stop(); pluginWrapper.setPluginState(PluginState.STOPPED); } catch (PluginException e) { @@ -193,16 +199,17 @@ public class DefaultPluginManager implements PluginManager { */ @Override public void loadPlugins() { + log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath()); // check for plugins directory if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { - log.error("No '" + pluginsDirectory + "' directory"); + log.error("No '{}' directory", pluginsDirectory.getAbsolutePath()); return; } // expand all plugin archives - FilenameFilter zipFilter = new ZipFilter(); - String[] zipFiles = pluginsDirectory.list(zipFilter); - for (String zipFile : zipFiles) { + FileFilter zipFilter = new ZipFileFilter(); + File[] zipFiles = pluginsDirectory.listFiles(zipFilter); + for (File zipFile : zipFiles) { try { expandPluginArchive(zipFile); } catch (IOException e) { @@ -211,15 +218,18 @@ public class DefaultPluginManager implements PluginManager { } // check for no plugins - FilenameFilter directoryFilter = new DirectoryFilter(); - String[] directories = pluginsDirectory.list(directoryFilter); + List<FileFilter> filterList = new ArrayList<FileFilter>(); + filterList.add(new DirectoryFileFilter()); + filterList.add(new NotFileFilter(createHiddenPluginFilter())); + FileFilter pluginsFilter = new AndFileFilter(filterList); + File[] directories = pluginsDirectory.listFiles(pluginsFilter); if (directories.length == 0) { log.info("No plugins"); return; } // load any plugin from plugins directory - for (String directory : directories) { + for (File directory : directories) { try { loadPlugin(directory); } catch (PluginException e) { @@ -268,15 +278,43 @@ public class DefaultPluginManager implements PluginManager { return null; } - private void loadPlugin(String fileName) throws PluginException { - // test for plugin directory - File pluginDirectory = new File(pluginsDirectory, fileName); - if (!pluginDirectory.isDirectory()) { - return; - } - + /** + * Add the possibility to override the PluginDescriptorFinder. + */ + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return new DefaultPluginDescriptorFinder(pluginClasspath); + } + + /** + * Add the possibility to override the ExtensionFinder. + */ + protected ExtensionFinder createExtensionFinder() { + return new DefaultExtensionFinder(compoundClassLoader); + } + + /** + * Add the possibility to override the PluginClassPath. + */ + protected PluginClasspath createPluginClasspath() { + return new PluginClasspath(); + } + + protected boolean isPluginDisabled(String pluginId) { + if (enabledPlugins.isEmpty()) { + return disabledPlugins.contains(pluginId); + } + + return !enabledPlugins.contains(pluginId); + } + + protected FileFilter createHiddenPluginFilter() { + return new HiddenFilter(); + } + + private void loadPlugin(File pluginDirectory) throws PluginException { // try to load the plugin - String pluginPath = "/".concat(fileName); + String pluginName = pluginDirectory.getName(); + String pluginPath = "/".concat(pluginName); // test for plugin duplication if (plugins.get(pathToIdMap.get(pluginPath)) != null) { @@ -284,28 +322,28 @@ public class DefaultPluginManager implements PluginManager { } // retrieves the plugin descriptor - log.debug("Find plugin descriptor '" + pluginPath + "'"); + log.debug("Find plugin descriptor '{}'", pluginPath); PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory); log.debug("Descriptor " + pluginDescriptor); String pluginClassName = pluginDescriptor.getPluginClass(); - log.debug("Class '" + pluginClassName + "'" + " for plugin '" + pluginPath + "'"); + log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); // test for disabled plugin if (isPluginDisabled(pluginDescriptor.getPluginId())) { - log.info("Plugin '" + pluginPath + "' is disabled"); + log.info("Plugin '{}' is disabled", pluginPath); return; } // load plugin - log.debug("Loading plugin '" + pluginPath + "'"); - PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory); + log.debug("Loading plugin '{}'", pluginPath); + PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath); pluginLoader.load(); - log.debug("Loaded plugin '" + pluginPath + "'"); + log.debug("Loaded plugin '{}'", pluginPath); // create the plugin wrapper - log.debug("Creating wrapper for plugin '" + pluginPath + "'"); + log.debug("Creating wrapper for plugin '{}'", pluginPath); PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); - log.debug("Created wrapper '" + pluginWrapper + "' for plugin '" + pluginPath + "'"); + log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); String pluginId = pluginDescriptor.getPluginId(); @@ -318,37 +356,15 @@ public class DefaultPluginManager implements PluginManager { pluginClassLoaders.put(pluginId, pluginClassLoader); } - /** - * Add the possibility to override the PluginDescriptorFinder. - */ - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return new DefaultPluginDescriptorFinder(); - } - - /** - * Add the possibility to override the ExtensionFinder. - */ - protected ExtensionFinder createExtensionFinder() { - return new DefaultExtensionFinder(compoundClassLoader); - } - - protected boolean isPluginDisabled(String pluginId) { - if (enabledPlugins.isEmpty()) { - return disabledPlugins.contains(pluginId); - } - - return !enabledPlugins.contains(pluginId); - } - - private void expandPluginArchive(String fileName) throws IOException { - File pluginArchiveFile = new File(pluginsDirectory, fileName); + private void expandPluginArchive(File pluginArchiveFile) throws IOException { + String fileName = pluginArchiveFile.getName(); long pluginArchiveDate = pluginArchiveFile.lastModified(); String pluginName = fileName.substring(0, fileName.length() - 4); File pluginDirectory = new File(pluginsDirectory, pluginName); // check if exists directory or the '.zip' file is "newer" than directory if (!pluginDirectory.exists() || (pluginArchiveDate > pluginDirectory.lastModified())) { - log.debug("Expand plugin archive '" + pluginArchiveFile + "' in '" + pluginDirectory + "'"); - // create directorie for plugin + log.debug("Expand plugin archive '{}' in '{}'", pluginArchiveFile, pluginDirectory); + // create directory for plugin pluginDirectory.mkdirs(); // expand '.zip' file @@ -369,7 +385,7 @@ public class DefaultPluginManager implements PluginManager { for (PluginWrapper pluginWrapper : resolvedPlugins) { unresolvedPlugins.remove(pluginWrapper); compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader()); - log.info("Plugin '" + pluginWrapper.getDescriptor().getPluginId() + "' resolved"); + log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java index 8611fef..a64aef5 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java @@ -51,14 +51,14 @@ class DependencyResolver { } } - log.debug("Graph: " + graph); + log.debug("Graph: {}", graph); List<String> pluginsId = graph.reverseTopologicalSort(); if (pluginsId == null) { throw new CyclicDependencyException("Cyclic dependences !!!" + graph.toString()); } - log.debug("Plugins order: " + pluginsId); + log.debug("Plugins order: {}", pluginsId); List<PluginWrapper> sortedPlugins = new ArrayList<PluginWrapper>(); for (String pluginId : pluginsId) { sortedPlugins.add(getPlugin(pluginId)); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java new file mode 100644 index 0000000..fd82793 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.fortsoft.pf4j.util.StringUtils; + +/** + * Read the plugin descriptor from the manifest file. + * + * @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; + } + + @Override + public PluginDescriptor find(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); + } + } + + PluginDescriptor pluginDescriptor = new PluginDescriptor(); + + // TODO validate !!! + Attributes attrs = manifest.getMainAttributes(); + String id = attrs.getValue("Plugin-Id"); + if (StringUtils.isEmpty(id)) { + throw new PluginException("Plugin-Id cannot be empty"); + } + pluginDescriptor.setPluginId(id); + + String clazz = attrs.getValue("Plugin-Class"); + if (StringUtils.isEmpty(clazz)) { + throw new PluginException("Plugin-Class cannot be empty"); + } + pluginDescriptor.setPluginClass(clazz); + + String version = attrs.getValue("Plugin-Version"); + if (StringUtils.isEmpty(version)) { + throw new PluginException("Plugin-Version cannot be empty"); + } + pluginDescriptor.setPluginVersion(PluginVersion.createVersion(version)); + + String provider = attrs.getValue("Plugin-Provider"); + pluginDescriptor.setProvider(provider); + String dependencies = attrs.getValue("Plugin-Dependencies"); + pluginDescriptor.setDependencies(dependencies); + + return pluginDescriptor; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java new file mode 100644 index 0000000..23cd8f6 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import java.util.ArrayList; +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 relativ to plugin repository. + * The default values are "classes" and "lib". + * + * @author Decebal Suiu + */ +public class PluginClasspath { + + private static final String DEFAULT_CLASSES_DIRECTORY = "classes"; + private static final String DEFAULT_LIB_DIRECTORY = "lib"; + + private List<String> classesDirectories; + private List<String> libDirectories; + + public PluginClasspath() { + classesDirectories = new ArrayList<String>(); + libDirectories = new ArrayList<String>(); + + // add defaults + classesDirectories.add(DEFAULT_CLASSES_DIRECTORY); + libDirectories.add(DEFAULT_LIB_DIRECTORY); + } + + public List<String> getClassesDirectories() { + return classesDirectories; + } + + public void setClassesDirectories(List<String> classesDirectories) { + this.classesDirectories = classesDirectories; + } + + public List<String> getLibDirectories() { + return libDirectories; + } + + public void setLibDirectories(List<String> libDirectories) { + this.libDirectories = libDirectories; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java index 78603f4..8bd2daa 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java @@ -13,15 +13,16 @@ package ro.fortsoft.pf4j; import java.io.File; -import java.io.FilenameFilter; +import java.io.FileFilter; import java.net.MalformedURLException; +import java.util.List; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.DirectoryFilter; -import ro.fortsoft.pf4j.util.JarFilter; +import ro.fortsoft.pf4j.util.DirectoryFileFilter; +import ro.fortsoft.pf4j.util.JarFileFilter; /** * Load all informations needed by a plugin. @@ -40,25 +41,15 @@ class PluginLoader { */ private File pluginRepository; - /* - * The directory with '.class' files. - */ - private File classesDirectory; - - /* - * The directory with '.jar' files. - */ - private File libDirectory; - + private PluginClasspath pluginClasspath; private PluginClassLoader pluginClassLoader; - public PluginLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, File pluginRepository) { + public PluginLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, File pluginRepository, PluginClasspath pluginClasspath) { this.pluginRepository = pluginRepository; - classesDirectory = new File(pluginRepository, "classes"); - libDirectory = new File(pluginRepository, "lib"); + this.pluginClasspath = pluginClasspath; ClassLoader parent = getClass().getClassLoader(); pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, parent); - log.debug("Created class loader " + pluginClassLoader); + log.debug("Created class loader {}", pluginClassLoader); } public File getPluginRepository() { @@ -77,66 +68,76 @@ class PluginLoader { return loadClasses() && loadJars(); } - private void getJars(Vector<String> v, File file) { - FilenameFilter jarFilter = new JarFilter(); - FilenameFilter directoryFilter = new DirectoryFilter(); - - if (file.exists() && file.isDirectory() && file.isAbsolute()) { - String[] jars = file.list(jarFilter); - for (int i = 0; (jars != null) && (i < jars.length); ++i) { - v.addElement(jars[i]); - } - - String[] directoryList = file.list(directoryFilter); - for (int i = 0; (directoryList != null) && (i < directoryList.length); ++i) { - File directory = new File(file, directoryList[i]); - getJars(v, directory); - } - } - } - private boolean loadClasses() { - // make 'classesDirectory' absolute - classesDirectory = classesDirectory.getAbsoluteFile(); - - if (classesDirectory.exists() && classesDirectory.isDirectory()) { - log.debug("Found '" + classesDirectory.getPath() + "' directory"); - - try { - pluginClassLoader.addURL(classesDirectory.toURI().toURL()); - log.debug("Added '" + classesDirectory + "' to the class loader path"); - } catch (MalformedURLException e) { - e.printStackTrace(); - log.error(e.getMessage(), e); - return false; - } - } + List<String> 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' directory. + * Add all *.jar files from lib directories to class loader. */ private boolean loadJars() { - // make 'jarDirectory' absolute - libDirectory = libDirectory.getAbsoluteFile(); - - Vector<String> jars = new Vector<String>(); - getJars(jars, libDirectory); - for (String jar : jars) { - File jarFile = new File(libDirectory, jar); - try { - pluginClassLoader.addURL(jarFile.toURI().toURL()); - log.debug("Added '" + jarFile + "' to the class loader path"); - } catch (MalformedURLException e) { - e.printStackTrace(); - log.error(e.getMessage(), e); - return false; - } - } + List<String> 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<File> jars = new Vector<File>(); + 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<File> 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]); + } + + File[] directories = file.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/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java index 4f0e753..2c7c707 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java @@ -19,6 +19,9 @@ import java.io.IOException; import java.io.InputStream; import java.util.Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import ro.fortsoft.pf4j.util.StringUtils; /** @@ -28,10 +31,14 @@ import ro.fortsoft.pf4j.util.StringUtils; */ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder { + private static final Logger log = LoggerFactory.getLogger(PropertiesPluginDescriptorFinder.class); + + private static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; + private String propertiesFileName; public PropertiesPluginDescriptorFinder() { - this("plugin.properties"); + this(DEFAULT_PROPERTIES_FILE_NAME); } public PropertiesPluginDescriptorFinder(String propertiesFileName) { @@ -41,6 +48,7 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder @Override public PluginDescriptor find(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"); } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java new file mode 100644 index 0000000..3f5014c --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/SezpozExtensionFinder.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.java.sezpoz.Index; +import net.java.sezpoz.IndexItem; + +/** + * Using Sezpoz(http://sezpoz.java.net/) for extensions discovery. + * + * @author Decebal Suiu + */ +public class SezpozExtensionFinder implements ExtensionFinder { + + private static final Logger log = LoggerFactory.getLogger(SezpozExtensionFinder.class); + + private volatile List<IndexItem<Extension, Object>> indices; + private ClassLoader classLoader; + + public SezpozExtensionFinder(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public <T> List<ExtensionWrapper<T>> find(Class<T> type) { + log.debug("Find extensions for {}", type); + List<ExtensionWrapper<T>> result = new ArrayList<ExtensionWrapper<T>>(); + getIndices(); +// System.out.println("indices = "+ indices); + for (IndexItem<Extension, Object> item : indices) { + try { + AnnotatedElement element = item.element(); + Class<?> extensionType = (Class<?>) element; + log.debug("Checking extension type {}", extensionType); + if (type.isAssignableFrom(extensionType)) { + Object instance = item.instance(); + if (instance != null) { + log.debug("Added extension {}", extensionType); + result.add(new ExtensionWrapper<T>(type.cast(instance), item.annotation().ordinal())); + } + } + } catch (InstantiationException e) { + log.error(e.getMessage(), e); + } + } + + return result; + } + + private List<IndexItem<Extension, Object>> getIndices() { + if (indices == null) { + indices = new ArrayList<IndexItem<Extension, Object>>(); + Iterator<IndexItem<Extension, Object>> it = Index.load(Extension.class, Object.class, classLoader).iterator(); + while (it.hasNext()) { + indices.add(it.next()); + } + } + + return indices; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java new file mode 100644 index 0000000..b50e852 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j.util; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * This filter providing conditional AND logic across a list of + * file filters. This filter returns <code>true</code> if all filters in the + * list return <code>true</code>. Otherwise, it returns <code>false</code>. + * Checking of the file filter list stops when the first filter returns + * <code>false</code>. + * + * @author Decebal Suiu + */ +public class AndFileFilter implements FileFilter { + + /** The list of file filters. */ + private List<FileFilter> fileFilters; + + public AndFileFilter() { + this.fileFilters = new ArrayList<FileFilter>(); + } + + public AndFileFilter(List<FileFilter> fileFilters) { + this.fileFilters = new ArrayList<FileFilter>(fileFilters); + } + + public void addFileFilter(FileFilter fileFilter) { + fileFilters.add(fileFilter); + } + + public List<FileFilter> getFileFilters() { + return Collections.unmodifiableList(fileFilters); + } + + public boolean removeFileFilter(FileFilter fileFilter) { + return fileFilters.remove(fileFilter); + } + + public void setFileFilters(List<FileFilter> fileFilters) { + this.fileFilters = new ArrayList<FileFilter>(fileFilters); + } + + @Override + public boolean accept(File file) { + if (this.fileFilters.size() == 0) { + return false; + } + + for (Iterator<FileFilter> iter = this.fileFilters.iterator(); iter.hasNext();) { + FileFilter fileFilter = (FileFilter) iter.next(); + if (!fileFilter.accept(file)) { + return false; + } + } + + return true; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFileFilter.java index 54e1a95..3877222 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFilter.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFileFilter.java @@ -14,22 +14,17 @@ package ro.fortsoft.pf4j.util; import java.io.File; import java.io.FileFilter; -import java.io.FilenameFilter; /** + * Filter accepts files that are directories. + * * @author Decebal Suiu */ -public class DirectoryFilter implements FileFilter, FilenameFilter { +public class DirectoryFileFilter implements FileFilter { - /** - * Accepts any file ending in .jar. The case of the filename is ignored. - */ + @Override public boolean accept(File file) { return file.isDirectory(); } - public boolean accept(File dir, String name) { - return accept(new File(dir, name)); - } - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFileFilter.java index 22b999f..03ed0b7 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFilter.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFileFilter.java @@ -13,29 +13,25 @@ package ro.fortsoft.pf4j.util; import java.io.File; -import java.io.FilenameFilter; +import java.io.FileFilter; /** + * Filter accepts any file ending in extension. The case of the filename is ignored. + * * @author Decebal Suiu */ -public class ExtensionFilter implements FilenameFilter { +public class ExtensionFileFilter implements FileFilter { private String extension; - public ExtensionFilter(String extension) { + public ExtensionFileFilter(String extension) { this.extension = extension; } - /** - * Accepts any file ending in extension. The case of the filename is ignored. - */ + @Override public boolean accept(File file) { // perform a case insensitive check. return file.getName().toUpperCase().endsWith(extension.toUpperCase()); } - public boolean accept(File dir, String name) { - return accept(new File(dir, name)); - } - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java new file mode 100644 index 0000000..eed53f1 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2013 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * Filter that only accepts hidden files. + * + * @author decebal.suiu + */ +public class HiddenFilter implements FileFilter { + + @Override + public boolean accept(File file) { + return file.isHidden(); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFileFilter.java index 60c8859..5c1f0ca 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFilter.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFileFilter.java @@ -18,14 +18,14 @@ package ro.fortsoft.pf4j.util; * * @author Decebal Suiu */ -public class JarFilter extends ExtensionFilter { +public class JarFileFilter extends ExtensionFileFilter { /** * The extension that this filter will search for. */ private static final String JAR_EXTENSION = ".JAR"; - public JarFilter() { + public JarFileFilter() { super(JAR_EXTENSION); } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java new file mode 100644 index 0000000..68ff3e0 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * This filter produces a logical NOT of the filters specified. + * + * @author Decebal Suiu + */ +public class NotFileFilter implements FileFilter { + + private FileFilter filter; + + public NotFileFilter(FileFilter filter) { + this.filter = filter; + } + + @Override + public boolean accept(File file) { + return !filter.accept(file); + } + +} 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 e3f3ed3..74d60d5 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java @@ -61,7 +61,7 @@ public class Unzip { } public void extract() throws IOException { - log.debug("Extract content of " + source + " to " + destination); + log.debug("Extract content of '{}' to '{}'", source, destination); // delete destination file if exists removeDirectory(destination); @@ -91,7 +91,7 @@ public class Unzip { fos.close(); } } catch (FileNotFoundException e) { - log.error("File '" + zipEntry.getName() + "' not found"); + log.error("File '{}' not found", zipEntry.getName()); } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFileFilter.java index 4b10de3..9743fb6 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFilter.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFileFilter.java @@ -18,14 +18,14 @@ package ro.fortsoft.pf4j.util; * * @author Decebal Suiu */ -public class ZipFilter extends ExtensionFilter { +public class ZipFileFilter extends ExtensionFileFilter { /** * The extension that this filter will search for. */ private static final String ZIP_EXTENSION = ".ZIP"; - public ZipFilter() { + public ZipFileFilter() { super(ZIP_EXTENSION); } @@ -10,7 +10,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>ro.fortsoft.pf4j</groupId> <artifactId>pf4j-parent</artifactId> - <version>0.5-SNAPSHOT</version> + <version>0.6-SNAPSHOT</version> <packaging>pom</packaging> <name>PF4J Parent</name> <description>Plugin Framework for Java</description> diff --git a/run-demo.bat b/run-demo.bat index 05c53df..0edca59 100644 --- a/run-demo.bat +++ b/run-demo.bat @@ -12,8 +12,8 @@ mkdir demo-dist\plugins REM copy artifacts to demo-dist folder xcopy demo\app\target\pf4j-demo-app-*.zip demo-dist /s /i -xcopy demo\plugin1\target\pf4j-demo-plugin1-*.zip demo-dist\plugins /s -xcopy demo\plugin2\target\pf4j-demo-plugin2-*.zip demo-dist\plugins /s +xcopy demo\plugins\plugin1\target\pf4j-demo-plugin1-*.zip demo-dist\plugins /s +xcopy demo\plugins\plugin2\target\pf4j-demo-plugin2-*.zip demo-dist\plugins /s cd demo-dist diff --git a/run-demo.sh b/run-demo.sh index ce68e5b..724d68b 100755 --- a/run-demo.sh +++ b/run-demo.sh @@ -14,8 +14,8 @@ mkdir demo-dist/plugins # copy artifacts to demo-dist folder cp -r demo/app/target/pf4j-demo-*/* demo-dist/ -cp demo/plugin1/target/pf4j-demo-plugin1-*.zip demo-dist/plugins/ -cp demo/plugin2/target/pf4j-demo-plugin2-*.zip demo-dist/plugins/ +cp demo/plugins/plugin1/target/pf4j-demo-plugin1-*.zip demo-dist/plugins/ +cp demo/plugins/plugin2/target/pf4j-demo-plugin2-*.zip demo-dist/plugins/ # run demo cd demo-dist |