From 6df8db4c34abb0fbbeddda7cee39124a53c4cdf5 Mon Sep 17 00:00:00 2001 From: Decebal Suiu Date: Thu, 3 Oct 2013 16:01:16 +0300 Subject: [PATCH 1/3] add RuntimeMode with DEVELOPMENT and DEPLOYMENT values and working on DEVELOPMENT mode --- README.md | 33 +- .../main/java/ro/fortsoft/pf4j/demo/Boot.java | 212 ++--- demo/plugins/plugin1/pom.xml | 254 +++--- .../pf4j/demo/welcome/WelcomePlugin.java | 10 + demo/plugins/plugin2/pom.xml | 246 +++-- .../fortsoft/pf4j/demo/hello/HelloPlugin.java | 3 + demo/plugins/pom.xml | 143 ++- .../fortsoft/pf4j/DefaultPluginManager.java | 852 ++++++++++-------- .../pf4j/DevelopmentPluginClasspath.java | 36 + .../ro/fortsoft/pf4j/PluginClasspath.java | 13 +- .../java/ro/fortsoft/pf4j/PluginManager.java | 5 + .../java/ro/fortsoft/pf4j/PluginWrapper.java | 9 + .../java/ro/fortsoft/pf4j/RuntimeMode.java | 55 ++ 13 files changed, 1022 insertions(+), 849 deletions(-) create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java create mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java diff --git a/README.md b/README.md index 6542dcc..cf98f95 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ It's very simple to add pf4j in your application: In above code, I created a **DefaultPluginManager** (it's the default implementation for **PluginManager** interface) that loads and starts all active(resolved) plugins. -Each available plugin is loaded using a **PluginClassLoader**. -The **PluginClassLoader** contains only classes found in _classes_ and _lib_ folders of plugin and runtime classes and libraries of the required plugins. +Each available plugin is loaded using a different java class loader, **PluginClassLoader**. +The **PluginClassLoader** contains only classes found in **PluginClasspath** (default _classes_ and _lib_ folders) of plugin and runtime classes and libraries of the required/dependent plugins. The plugins are stored in a folder. You can specify the plugins folder in the constructor of DefaultPluginManager. If the plugins folder is not specified than the location is returned by `System.getProperty("pf4j.pluginsDir", "plugins")`. @@ -71,12 +71,12 @@ The structure of plugins folder is: * plugin2.zip (or plugin2 folder) In plugins folder you can put a plugin as folder or archive file (zip). -A plugin folder has this structure: +A plugin folder has this structure by default: * `classes` folder * `lib` folder (optional - if the plugin used third party libraries) The plugin manager searches plugins metadata using a **PluginDescriptorFinder**. -**DefaultPluginDescriptorFinder** lookups plugins descriptors in MANIFEST.MF file. +**DefaultPluginDescriptorFinder** is a "link" to **ManifestPluginDescriptorFinder** that lookups plugins descriptors in MANIFEST.MF file. In this case the `classes/META-INF/MANIFEST.MF` file looks like: Manifest-Version: 1.0 @@ -102,7 +102,7 @@ You can define an extension point in your application using **ExtensionPoint** i } Another important internal component is **ExtensionFinder** that describes how plugin manager discovers extensions for extensions points. -**DefaultExtensionFinder** looks up extensions using **Extension** annotation. +**DefaultExtensionFinder** is a "link" to **SezpozExtensionFinder** that looks up extensions using **Extension** annotation. public class WelcomePlugin extends Plugin { @@ -135,7 +135,7 @@ The output is: >>> Welcome >>> Hello -You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder) in DefaultPluginManager just override createXXX methods (factory method pattern). +You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, ...) in DefaultPluginManager just override createXXX methods (factory method pattern). Example: @@ -154,6 +154,23 @@ and in plugin respository you must have a plugin.properties file with the below For more information please see the demo sources. +Development runtime mode +-------------------------- +PF4J can run in two modes: **DEVELOPMENT** and **DEPLOYMENT**. +The DEPLOYMENT(default) mode is the standard workflow for plugins creation: create a new maven module for each plugin, codding the plugin (declares new extension points and/or +add new extensions), pack the plugin in a zip file, deploy the zip file to plugins folder. These operations are time consuming and from this reason I introduced the DEVELOPMENT runtime mode. +The main advantage of DEVELOPMENT runtime mode for a plugin developer is that he/she is not enforced to pack and deploy the plugins. In DEVELOPMENT mode you can developing plugins in a simple and fast mode. + +Lets describe how DEVELOPMENT runtime mode works. + +First, you can change the runtime mode using the "pf4j.mode" system property or overriding __DefaultPluginManager.getRuntimeMode()__. +For example I run the pf4j demo in eclipse in DEVELOPMENT mode adding only `"-Dpf4j.mode=development"` to the pf4j demo launcher. +You can retrieve the current runtime mode using __PluginManager.getRuntimeMode()__ or in your Plugin implementation with __getWrapper().getRuntimeMode()__(see [WelcomeMessage](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)). +The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is "../plugins", PropertiesPluginDescriptorFinder as PluginDescriptorFinder, DevelopmentPluginClasspath as PluginClassPath). +Another advantage of DEVELOPMENT runtime mode is that you can execute some code lines only in this mode (for example more debug messages). + +For more details see the demo application. + Enable/Disable plugins ------------------- In theory, it's a relation **1:N** between an extension point and the extensions for this extension point. @@ -191,8 +208,8 @@ If a file with enabled.txt exists than disabled.txt is ignored. See enabled.txt Demo ------------------- I have a tiny demo application. The demo application is in demo folder. -In demo/api folder I declared an extension point (_Greeting_). -In demo/plugin* I implemented two plugins: plugin1, plugin2 (each plugin adds an extension for _Greeting_). +In demo/api folder I declared an extension point ( _Greeting_). +In demo/plugins I implemented two plugins: plugin1, plugin2 (each plugin adds an extension for _Greeting_). To run the demo application use: 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 7681a75..8dde182 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 @@ -1,145 +1,67 @@ -/* - * 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.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; - -/** - * A boot class that start the demo. - * - * @author Decebal Suiu - */ -public class Boot { - - public static void main(String[] args) { - // print logo - printLogo(); - - // 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 greetings = pluginManager.getExtensions(Greeting.class); - for (Greeting greeting : greetings) { - System.out.println(">>> " + greeting.getGreeting()); - } - - // stop the plugins - pluginManager.stopPlugins(); - /* - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - pluginManager.stopPlugins(); - } - - }); - */ - } - - 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 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 map = new HashMap(); - - 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 + "'"); - } - - } - -} +/* + * 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.demo; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.PluginManager; +import ro.fortsoft.pf4j.demo.api.Greeting; + +/** + * A boot class that start the demo. + * + * @author Decebal Suiu + */ +public class Boot { + + public static void main(String[] args) { + // print logo + printLogo(); + + // create the plugin manager + final PluginManager pluginManager = new DefaultPluginManager(); + + // load and start (active/resolved) the plugins + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + + // retrieves the extensions for Greeting extension point + List greetings = pluginManager.getExtensions(Greeting.class); + for (Greeting greeting : greetings) { + System.out.println(">>> " + greeting.getGreeting()); + } + + // stop the plugins + pluginManager.stopPlugins(); + /* + Runtime.getRuntime().addShutdownHook(new Thread() { + + @Override + public void run() { + pluginManager.stopPlugins(); + } + + }); + */ + } + + private static void printLogo() { + System.out.println(StringUtils.repeat("#", 40)); + System.out.println(StringUtils.center("PF4J-DEMO", 40)); + System.out.println(StringUtils.repeat("#", 40)); + } + +} diff --git a/demo/plugins/plugin1/pom.xml b/demo/plugins/plugin1/pom.xml index 8fe5f7b..bf8abe5 100644 --- a/demo/plugins/plugin1/pom.xml +++ b/demo/plugins/plugin1/pom.xml @@ -1,131 +1,123 @@ - - - - - ro.fortsoft.pf4j.demo - pf4j-demo-plugins - 0.6-SNAPSHOT - - - 4.0.0 - pf4j-demo-plugin1 - 0.6-SNAPSHOT - jar - Demo Plugin #1 - - - welcome-plugin - ro.fortsoft.pf4j.demo.welcome.WelcomePlugin - 0.0.1 - Decebal Suiu - - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.6 - - - unzip jar file - package - - - - - - - run - - - - - - - maven-assembly-plugin - 2.3 - - - - src/main/assembly/assembly.xml - - - false - - - - make-assembly - package - - attached - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - ${plugin.id} - ${plugin.class} - ${plugin.version} - ${plugin.provider} - ${plugin.dependencies} - - - - - - - maven-deploy-plugin - - true - - - - - - - - ro.fortsoft.pf4j - pf4j - ${project.version} - provided - - - - ro.fortsoft.pf4j.demo - pf4j-demo-api - ${project.version} - provided - - - - + + + + + ro.fortsoft.pf4j.demo + pf4j-demo-plugins + 0.6-SNAPSHOT + + + 4.0.0 + pf4j-demo-plugin1 + 0.6-SNAPSHOT + jar + Demo Plugin #1 + + + welcome-plugin + ro.fortsoft.pf4j.demo.welcome.WelcomePlugin + 0.0.1 + Decebal Suiu + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + unzip jar file + package + + + + + + + run + + + + + + + maven-assembly-plugin + 2.3 + + + + src/main/assembly/assembly.xml + + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.dependencies} + + + + + + + maven-deploy-plugin + + true + + + + + + + + commons-lang + commons-lang + 2.6 + + + + diff --git a/demo/plugins/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..1c35259 100644 --- a/demo/plugins/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 @@ -12,9 +12,12 @@ */ package ro.fortsoft.pf4j.demo.welcome; +import org.apache.commons.lang.StringUtils; + import ro.fortsoft.pf4j.Extension; import ro.fortsoft.pf4j.Plugin; import ro.fortsoft.pf4j.PluginWrapper; +import ro.fortsoft.pf4j.RuntimeMode; import ro.fortsoft.pf4j.demo.api.Greeting; /** @@ -26,10 +29,16 @@ public class WelcomePlugin extends Plugin { super(wrapper); } + @Override public void start() { System.out.println("WelcomePlugin.start()"); + // for testing the development mode + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + System.out.println(StringUtils.upperCase("WelcomePlugin")); + } } + @Override public void stop() { System.out.println("WelcomePlugin.stop()"); } @@ -37,6 +46,7 @@ public class WelcomePlugin extends Plugin { @Extension public static class WelcomeGreeting implements Greeting { + @Override public String getGreeting() { return "Welcome"; } diff --git a/demo/plugins/plugin2/pom.xml b/demo/plugins/plugin2/pom.xml index 2bf0dd8..4b84e9f 100644 --- a/demo/plugins/plugin2/pom.xml +++ b/demo/plugins/plugin2/pom.xml @@ -1,131 +1,115 @@ - - - - - ro.fortsoft.pf4j.demo - pf4j-demo-plugins - 0.6-SNAPSHOT - - - 4.0.0 - pf4j-demo-plugin2 - 0.6-SNAPSHOT - jar - Demo Plugin #2 - - - hello-plugin - ro.fortsoft.pf4j.demo.hello.HelloPlugin - 0.0.1 - Decebal Suiu - - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.6 - - - unzip jar file - package - - - - - - - run - - - - - - - maven-assembly-plugin - 2.3 - - - - src/main/assembly/assembly.xml - - - false - - - - make-assembly - package - - attached - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - ${plugin.id} - ${plugin.class} - ${plugin.version} - ${plugin.provider} - ${plugin.dependencies} - - - - - - - maven-deploy-plugin - - true - - - - - - - - ro.fortsoft.pf4j - pf4j - ${project.version} - provided - - - - ro.fortsoft.pf4j.demo - pf4j-demo-api - ${project.version} - provided - - - - + + + + + ro.fortsoft.pf4j.demo + pf4j-demo-plugins + 0.6-SNAPSHOT + + + 4.0.0 + pf4j-demo-plugin2 + 0.6-SNAPSHOT + jar + Demo Plugin #2 + + + hello-plugin + ro.fortsoft.pf4j.demo.hello.HelloPlugin + 0.0.1 + Decebal Suiu + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + unzip jar file + package + + + + + + + run + + + + + + + maven-assembly-plugin + 2.3 + + + + src/main/assembly/assembly.xml + + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.dependencies} + + + + + + + maven-deploy-plugin + + true + + + + + + diff --git a/demo/plugins/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..d8963fc 100644 --- a/demo/plugins/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 @@ -28,10 +28,12 @@ public class HelloPlugin extends Plugin { super(wrapper); } + @Override public void start() { System.out.println("HelloPlugin.start()"); } + @Override public void stop() { System.out.println("HelloPlugin.stop()"); } @@ -39,6 +41,7 @@ public class HelloPlugin extends Plugin { @Extension public static class HelloGreeting implements Greeting { + @Override public String getGreeting() { return "Hello"; } diff --git a/demo/plugins/pom.xml b/demo/plugins/pom.xml index d451b31..05f6c27 100644 --- a/demo/plugins/pom.xml +++ b/demo/plugins/pom.xml @@ -1,37 +1,106 @@ - - - - - ro.fortsoft.pf4j.demo - pf4j-demo-parent - 0.6-SNAPSHOT - - - 4.0.0 - ro.fortsoft.pf4j.demo - pf4j-demo-plugins - 0.6-SNAPSHOT - pom - Demo Plugins Parent - - - - - false - src/main/java - - **/*.java - - - - src/main/resources - - - - - - plugin1 - plugin2 - - - + + + + + ro.fortsoft.pf4j.demo + pf4j-demo-parent + 0.6-SNAPSHOT + + + 4.0.0 + ro.fortsoft.pf4j.demo + pf4j-demo-plugins + 0.6-SNAPSHOT + pom + Demo Plugins Parent + + + + + false + src/main/java + + **/*.java + + + + src/main/resources + + + + + + maven-dependency-plugin + + + process-sources + + copy-dependencies + + + ${project.build.directory}/lib + provided + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + [2.0,) + + copy-dependencies + + + + + + true + true + + + + + + + + + + + + + plugin1 + plugin2 + + + + + ro.fortsoft.pf4j + pf4j + ${project.version} + provided + + + + ro.fortsoft.pf4j.demo + pf4j-demo-api + ${project.version} + provided + + + + diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index 0c2628d..30658cd 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -1,392 +1,460 @@ -/* - * Copyright 2012 Decebal Suiu - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with - * the License. You may obtain a copy of the License in the LICENSE file, or at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package ro.fortsoft.pf4j; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ro.fortsoft.pf4j.util.AndFileFilter; -import ro.fortsoft.pf4j.util.CompoundClassLoader; -import ro.fortsoft.pf4j.util.DirectoryFileFilter; -import ro.fortsoft.pf4j.util.FileUtils; -import ro.fortsoft.pf4j.util.HiddenFilter; -import ro.fortsoft.pf4j.util.NotFileFilter; -import ro.fortsoft.pf4j.util.Unzip; -import ro.fortsoft.pf4j.util.ZipFileFilter; - -/** - * Default implementation of the PluginManager interface. - * - * @author Decebal Suiu - */ -public class DefaultPluginManager implements PluginManager { - - private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); - - /** - * The plugins repository. - */ - private File pluginsDirectory; - - private final ExtensionFinder extensionFinder; - - private final PluginDescriptorFinder pluginDescriptorFinder; - - private final PluginClasspath pluginClasspath; - - /** - * A map of plugins this manager is responsible for (the key is the 'pluginId'). - */ - private Map plugins; - - /** - * A map of plugin class loaders (he key is the 'pluginId'). - */ - private Map pluginClassLoaders; - - /** - * A relation between 'pluginPath' and 'pluginId' - */ - private Map pathToIdMap; - - /** - * 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; - - private List enabledPlugins; - private List disabledPlugins; - - /** - * A compound class loader of resolved plugins. - */ - protected CompoundClassLoader compoundClassLoader; - - /** - * Th plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins"). - */ - public DefaultPluginManager() { - this(new File(System.getProperty("pf4j.pluginsDir", "plugins"))); - } - - /** - * Constructs DefaultPluginManager which the given plugins directory. - * - * @param pluginsDirectory - * the directory to search for plugins - */ - public DefaultPluginManager(File pluginsDirectory) { - this.pluginsDirectory = pluginsDirectory; - - plugins = new HashMap(); - pluginClassLoaders = new HashMap(); - pathToIdMap = new HashMap(); - unresolvedPlugins = new ArrayList(); - resolvedPlugins = new ArrayList(); - startedPlugins = new ArrayList(); - disabledPlugins = new ArrayList(); - compoundClassLoader = new CompoundClassLoader(); - - pluginClasspath = createPluginClasspath(); - pluginDescriptorFinder = createPluginDescriptorFinder(); - extensionFinder = createExtensionFinder(); - - try { - // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file) - enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true); - log.info("Enabled plugins: {}", enabledPlugins); - - // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file) - disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true); - log.info("Disabled plugins: {}", disabledPlugins); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - - System.setProperty("pf4j.pluginsDir", pluginsDirectory.getAbsolutePath()); - } - - @Override - public List getPlugins() { - return new ArrayList(plugins.values()); - } - - @Override - public List getResolvedPlugins() { - return resolvedPlugins; - } - - public PluginWrapper getPlugin(String pluginId) { - return plugins.get(pluginId); - } - - @Override - public List getUnresolvedPlugins() { - return unresolvedPlugins; - } - - @Override - public List getStartedPlugins() { - return startedPlugins; - } - - /** - * Start all active plugins. - */ - @Override - public void startPlugins() { - for (PluginWrapper pluginWrapper : resolvedPlugins) { - try { - log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); - pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); - startedPlugins.add(pluginWrapper); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - } - - /** - * Stop all active plugins. - */ - @Override - public void stopPlugins() { - // stop started plugins in reverse order - Collections.reverse(startedPlugins); - for (PluginWrapper pluginWrapper : startedPlugins) { - try { - log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - } - - /** - * Load plugins. - */ - @Override - public void loadPlugins() { - log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath()); - // check for plugins directory - if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { - log.error("No '{}' directory", pluginsDirectory.getAbsolutePath()); - return; - } - - // expand all plugin archives - FileFilter zipFilter = new ZipFileFilter(); - File[] zipFiles = pluginsDirectory.listFiles(zipFilter); - for (File zipFile : zipFiles) { - try { - expandPluginArchive(zipFile); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - - // check for no plugins - List filterList = new ArrayList(); - filterList.add(new DirectoryFileFilter()); - filterList.add(new NotFileFilter(createHiddenPluginFilter())); - FileFilter pluginsFilter = new AndFileFilter(filterList); - File[] directories = pluginsDirectory.listFiles(pluginsFilter); - if (directories.length == 0) { - log.info("No plugins"); - return; - } - - // load any plugin from plugins directory - for (File directory : directories) { - try { - loadPlugin(directory); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - - // resolve 'unresolvedPlugins' - try { - resolvePlugins(); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - - /** - * Get plugin class loader for this path. - */ - @Override - public PluginClassLoader getPluginClassLoader(String pluginId) { - return pluginClassLoaders.get(pluginId); - } - - @Override - public List getExtensions(Class type) { - List> extensionsWrapper = extensionFinder.find(type); - List extensions = new ArrayList(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getInstance()); - } - - return extensions; - } - - /** - * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. - */ - public PluginWrapper whichPlugin(Class clazz) { - ClassLoader classLoader = clazz.getClassLoader(); - for (PluginWrapper plugin : resolvedPlugins) { - if (plugin.getPluginClassLoader() == classLoader) { - return plugin; - } - } - - return null; - } - - /** - * Add the possibility to override the PluginDescriptorFinder. - */ - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return new DefaultPluginDescriptorFinder(pluginClasspath); - } - - /** - * Add the possibility to override the ExtensionFinder. - */ - protected ExtensionFinder createExtensionFinder() { - return new DefaultExtensionFinder(compoundClassLoader); - } - - /** - * Add the possibility to override the PluginClassPath. - */ - protected PluginClasspath createPluginClasspath() { - return new PluginClasspath(); - } - - protected boolean isPluginDisabled(String pluginId) { - if (enabledPlugins.isEmpty()) { - return disabledPlugins.contains(pluginId); - } - - return !enabledPlugins.contains(pluginId); - } - - protected FileFilter createHiddenPluginFilter() { - return new HiddenFilter(); - } - - private void loadPlugin(File pluginDirectory) throws PluginException { - // try to load the plugin - String pluginName = pluginDirectory.getName(); - String pluginPath = "/".concat(pluginName); - - // test for plugin duplication - if (plugins.get(pathToIdMap.get(pluginPath)) != null) { - return; - } - - // retrieves the plugin descriptor - log.debug("Find plugin descriptor '{}'", pluginPath); - PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory); - log.debug("Descriptor " + pluginDescriptor); - String pluginClassName = pluginDescriptor.getPluginClass(); - log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); - - // test for disabled plugin - if (isPluginDisabled(pluginDescriptor.getPluginId())) { - log.info("Plugin '{}' is disabled", pluginPath); - return; - } - - // load plugin - log.debug("Loading plugin '{}'", pluginPath); - PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath); - pluginLoader.load(); - log.debug("Loaded plugin '{}'", pluginPath); - - // create the plugin wrapper - log.debug("Creating wrapper for plugin '{}'", pluginPath); - PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); - log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); - - String pluginId = pluginDescriptor.getPluginId(); - - // add plugin to the list with plugins - plugins.put(pluginId, pluginWrapper); - unresolvedPlugins.add(pluginWrapper); - - // add plugin class loader to the list with class loaders - PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); - pluginClassLoaders.put(pluginId, pluginClassLoader); - } - - private void expandPluginArchive(File pluginArchiveFile) throws IOException { - String fileName = pluginArchiveFile.getName(); - long pluginArchiveDate = pluginArchiveFile.lastModified(); - String pluginName = fileName.substring(0, fileName.length() - 4); - File pluginDirectory = new File(pluginsDirectory, pluginName); - // check if exists directory or the '.zip' file is "newer" than directory - if (!pluginDirectory.exists() || (pluginArchiveDate > pluginDirectory.lastModified())) { - log.debug("Expand plugin archive '{}' in '{}'", pluginArchiveFile, pluginDirectory); - // create directory for plugin - pluginDirectory.mkdirs(); - - // expand '.zip' file - Unzip unzip = new Unzip(); - unzip.setSource(pluginArchiveFile); - unzip.setDestination(pluginDirectory); - unzip.extract(); - } - } - - private void resolvePlugins() throws PluginException { - resolveDependencies(); - } - - private void resolveDependencies() throws PluginException { - DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); - resolvedPlugins = dependencyResolver.getSortedPlugins(); - for (PluginWrapper pluginWrapper : resolvedPlugins) { - unresolvedPlugins.remove(pluginWrapper); - compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader()); - log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); - } - } - -} +/* + * Copyright 2012 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.fortsoft.pf4j.util.AndFileFilter; +import ro.fortsoft.pf4j.util.CompoundClassLoader; +import ro.fortsoft.pf4j.util.DirectoryFileFilter; +import ro.fortsoft.pf4j.util.FileUtils; +import ro.fortsoft.pf4j.util.HiddenFilter; +import ro.fortsoft.pf4j.util.NotFileFilter; +import ro.fortsoft.pf4j.util.Unzip; +import ro.fortsoft.pf4j.util.ZipFileFilter; + +/** + * Default implementation of the PluginManager interface. + * + * @author Decebal Suiu + */ +public class DefaultPluginManager implements PluginManager { + + private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); + + public static final String DEFAULT_PLUGINS_DIRECTORY = "plugins"; + public static final String DEVELOPMENT_PLUGINS_DIRECTORY = "../plugins"; + + /** + * The plugins repository. + */ + private File pluginsDirectory; + + private ExtensionFinder extensionFinder; + + private PluginDescriptorFinder pluginDescriptorFinder; + + private PluginClasspath pluginClasspath; + + /** + * A map of plugins this manager is responsible for (the key is the 'pluginId'). + */ + private Map plugins; + + /** + * A map of plugin class loaders (he key is the 'pluginId'). + */ + private Map pluginClassLoaders; + + /** + * A relation between 'pluginPath' and 'pluginId' + */ + private Map pathToIdMap; + + /** + * 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; + + private List enabledPlugins; + private List disabledPlugins; + + /** + * A compound class loader of resolved plugins. + */ + protected CompoundClassLoader compoundClassLoader; + + /** + * Cache value for the runtime mode. No need to re-read it because it wont change at + * runtime. + */ + private RuntimeMode runtimeMode; + + /** + * The plugins directory is supplied by System.getProperty("pf4j.pluginsDir", "plugins"). + */ + public DefaultPluginManager() { + this.pluginsDirectory = createPluginsDirectory(); + + initialize(); + } + + /** + * Constructs DefaultPluginManager which the given plugins directory. + * + * @param pluginsDirectory + * the directory to search for plugins + */ + public DefaultPluginManager(File pluginsDirectory) { + this.pluginsDirectory = pluginsDirectory; + + initialize(); + } + + @Override + public List getPlugins() { + return new ArrayList(plugins.values()); + } + + @Override + public List getResolvedPlugins() { + return resolvedPlugins; + } + + public PluginWrapper getPlugin(String pluginId) { + return plugins.get(pluginId); + } + + @Override + public List getUnresolvedPlugins() { + return unresolvedPlugins; + } + + @Override + public List getStartedPlugins() { + return startedPlugins; + } + + /** + * Start all active plugins. + */ + @Override + public void startPlugins() { + for (PluginWrapper pluginWrapper : resolvedPlugins) { + try { + log.info("Start plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + startedPlugins.add(pluginWrapper); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + } + + /** + * Stop all active plugins. + */ + @Override + public void stopPlugins() { + // stop started plugins in reverse order + Collections.reverse(startedPlugins); + for (PluginWrapper pluginWrapper : startedPlugins) { + try { + log.info("Stop plugin '{}'", pluginWrapper.getDescriptor().getPluginId()); + pluginWrapper.getPlugin().stop(); + pluginWrapper.setPluginState(PluginState.STOPPED); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + } + + /** + * Load plugins. + */ + @Override + public void loadPlugins() { + log.debug("Lookup plugins in '{}'", pluginsDirectory.getAbsolutePath()); + // check for plugins directory + if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) { + log.error("No '{}' directory", pluginsDirectory.getAbsolutePath()); + return; + } + + // expand all plugin archives + FileFilter zipFilter = new ZipFileFilter(); + File[] zipFiles = pluginsDirectory.listFiles(zipFilter); + for (File zipFile : zipFiles) { + try { + expandPluginArchive(zipFile); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + + // check for no plugins + List filterList = new ArrayList(); + filterList.add(new DirectoryFileFilter()); + filterList.add(new NotFileFilter(createHiddenPluginFilter())); + FileFilter pluginsFilter = new AndFileFilter(filterList); + File[] directories = pluginsDirectory.listFiles(pluginsFilter); + log.debug("Possible plugins: {}", Arrays.asList(directories)); + if (directories.length == 0) { + log.info("No plugins"); + return; + } + + // load any plugin from plugins directory + for (File directory : directories) { + try { + loadPlugin(directory); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + + // resolve 'unresolvedPlugins' + try { + resolvePlugins(); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + + /** + * Get plugin class loader for this path. + */ + @Override + public PluginClassLoader getPluginClassLoader(String pluginId) { + return pluginClassLoaders.get(pluginId); + } + + @Override + public List getExtensions(Class type) { + List> extensionsWrapper = extensionFinder.find(type); + List extensions = new ArrayList(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getInstance()); + } + + return extensions; + } + + @Override + public RuntimeMode getRuntimeMode() { + if (runtimeMode == null) { + // retrieves the runtime mode from system + String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString()); + runtimeMode = RuntimeMode.byName(modeAsString); + + log.info("PF4J runtime mode: '" + runtimeMode + "'"); + + } + + return runtimeMode; + } + + /** + * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. + */ + public PluginWrapper whichPlugin(Class clazz) { + ClassLoader classLoader = clazz.getClassLoader(); + for (PluginWrapper plugin : resolvedPlugins) { + if (plugin.getPluginClassLoader() == classLoader) { + return plugin; + } + } + + return null; + } + + /** + * Add the possibility to override the PluginDescriptorFinder. + * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a + * PropertiesPluginDescriptorFinder is returned else this method returns + * DefaultPluginDescriptorFinder. + */ + protected PluginDescriptorFinder createPluginDescriptorFinder() { + if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { + return new PropertiesPluginDescriptorFinder(); + } + + return new DefaultPluginDescriptorFinder(pluginClasspath); + } + + /** + * Add the possibility to override the ExtensionFinder. + */ + protected ExtensionFinder createExtensionFinder() { + return new DefaultExtensionFinder(compoundClassLoader); + } + + /** + * Add the possibility to override the PluginClassPath. + * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a + * DevelopmentPluginClasspath is returned else this method returns + * PluginClasspath. + */ + protected PluginClasspath createPluginClasspath() { + if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { + return new DevelopmentPluginClasspath(); + } + + return new PluginClasspath(); + } + + protected boolean isPluginDisabled(String pluginId) { + if (enabledPlugins.isEmpty()) { + return disabledPlugins.contains(pluginId); + } + + return !enabledPlugins.contains(pluginId); + } + + protected FileFilter createHiddenPluginFilter() { + return new HiddenFilter(); + } + + /** + * Add the possibility to override the plugins directory. + * If a "pf4j.pluginsDir" system property is defined than this method returns + * that directory. + * If getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a + * DEVELOPMENT_PLUGINS_DIRECTORY ("../plugins") is returned else this method returns + * DEFAULT_PLUGINS_DIRECTORY ("plugins"). + * @return + */ + protected File createPluginsDirectory() { + String pluginsDir = System.getProperty("pf4j.pluginsDir"); + if (pluginsDir == null) { + if (RuntimeMode.DEVELOPMENT.equals(getRuntimeMode())) { + pluginsDir = DEVELOPMENT_PLUGINS_DIRECTORY; + } else { + pluginsDir = DEFAULT_PLUGINS_DIRECTORY; + } + } + + return new File(pluginsDir); + } + + private void initialize() { + plugins = new HashMap(); + pluginClassLoaders = new HashMap(); + pathToIdMap = new HashMap(); + unresolvedPlugins = new ArrayList(); + resolvedPlugins = new ArrayList(); + startedPlugins = new ArrayList(); + disabledPlugins = new ArrayList(); + compoundClassLoader = new CompoundClassLoader(); + + pluginClasspath = createPluginClasspath(); + pluginDescriptorFinder = createPluginDescriptorFinder(); + extensionFinder = createExtensionFinder(); + + try { + // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file) + enabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "enabled.txt"), true); + log.info("Enabled plugins: {}", enabledPlugins); + + // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file) + disabledPlugins = FileUtils.readLines(new File(pluginsDirectory, "disabled.txt"), true); + log.info("Disabled plugins: {}", disabledPlugins); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + + System.setProperty("pf4j.pluginsDir", pluginsDirectory.getAbsolutePath()); + } + + private void loadPlugin(File pluginDirectory) throws PluginException { + // try to load the plugin + String pluginName = pluginDirectory.getName(); + String pluginPath = "/".concat(pluginName); + + // test for plugin duplication + if (plugins.get(pathToIdMap.get(pluginPath)) != null) { + return; + } + + // retrieves the plugin descriptor + log.debug("Find plugin descriptor '{}'", pluginPath); + PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginDirectory); + log.debug("Descriptor " + pluginDescriptor); + String pluginClassName = pluginDescriptor.getPluginClass(); + log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); + + // test for disabled plugin + if (isPluginDisabled(pluginDescriptor.getPluginId())) { + log.info("Plugin '{}' is disabled", pluginPath); + return; + } + + // load plugin + log.debug("Loading plugin '{}'", pluginPath); + PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath); + pluginLoader.load(); + log.debug("Loaded plugin '{}'", pluginPath); + + // create the plugin wrapper + log.debug("Creating wrapper for plugin '{}'", pluginPath); + PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); + pluginWrapper.setRuntimeMode(getRuntimeMode()); + log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); + + String pluginId = pluginDescriptor.getPluginId(); + + // add plugin to the list with plugins + plugins.put(pluginId, pluginWrapper); + unresolvedPlugins.add(pluginWrapper); + + // add plugin class loader to the list with class loaders + PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); + pluginClassLoaders.put(pluginId, pluginClassLoader); + } + + private void expandPluginArchive(File pluginArchiveFile) throws IOException { + String fileName = pluginArchiveFile.getName(); + long pluginArchiveDate = pluginArchiveFile.lastModified(); + String pluginName = fileName.substring(0, fileName.length() - 4); + File pluginDirectory = new File(pluginsDirectory, pluginName); + // check if exists directory or the '.zip' file is "newer" than directory + if (!pluginDirectory.exists() || (pluginArchiveDate > pluginDirectory.lastModified())) { + log.debug("Expand plugin archive '{}' in '{}'", pluginArchiveFile, pluginDirectory); + // create directory for plugin + pluginDirectory.mkdirs(); + + // expand '.zip' file + Unzip unzip = new Unzip(); + unzip.setSource(pluginArchiveFile); + unzip.setDestination(pluginDirectory); + unzip.extract(); + } + } + + private void resolvePlugins() throws PluginException { + resolveDependencies(); + } + + private void resolveDependencies() throws PluginException { + DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); + resolvedPlugins = dependencyResolver.getSortedPlugins(); + for (PluginWrapper pluginWrapper : resolvedPlugins) { + unresolvedPlugins.remove(pluginWrapper); + compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader()); + log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); + } + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java new file mode 100644 index 0000000..e14722c --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +/** + * Overwrite classes directories to "target/classes" and lib directories to "target/lib". + * + * @author Decebal Suiu + */ +public class DevelopmentPluginClasspath extends PluginClasspath { + + private static final String DEVELOPMENT_CLASSES_DIRECTORY = "target/classes"; + private static final String DEVELOPMENT_LIB_DIRECTORY = "target/lib"; + + public DevelopmentPluginClasspath() { + super(); + } + + @Override + protected void addResources() { + classesDirectories.add(DEVELOPMENT_CLASSES_DIRECTORY); + libDirectories.add(DEVELOPMENT_LIB_DIRECTORY); + } + + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java index 23cd8f6..5fbe975 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java @@ -28,16 +28,14 @@ public class PluginClasspath { private static final String DEFAULT_CLASSES_DIRECTORY = "classes"; private static final String DEFAULT_LIB_DIRECTORY = "lib"; - private List classesDirectories; - private List libDirectories; + protected List classesDirectories; + protected List libDirectories; public PluginClasspath() { classesDirectories = new ArrayList(); libDirectories = new ArrayList(); - // add defaults - classesDirectories.add(DEFAULT_CLASSES_DIRECTORY); - libDirectories.add(DEFAULT_LIB_DIRECTORY); + addResources(); } public List getClassesDirectories() { @@ -56,4 +54,9 @@ public class PluginClasspath { this.libDirectories = libDirectories; } + protected void addResources() { + classesDirectories.add(DEFAULT_CLASSES_DIRECTORY); + libDirectories.add(DEFAULT_LIB_DIRECTORY); + } + } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java index 5e89b6b..7c78b89 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java @@ -61,4 +61,9 @@ public interface PluginManager { public List getExtensions(Class type); + /** + * The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT. + */ + public RuntimeMode getRuntimeMode(); + } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java index 4dd18d1..d307cf0 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java @@ -27,6 +27,7 @@ public class PluginWrapper { PluginClassLoader pluginClassLoader; Plugin plugin; PluginState pluginState; + RuntimeMode runtimeMode; public PluginWrapper(PluginDescriptor descriptor, String pluginPath, PluginClassLoader pluginClassLoader) { this.descriptor = descriptor; @@ -74,6 +75,10 @@ public class PluginWrapper { return pluginState; } + public RuntimeMode getRuntimeMode() { + return runtimeMode; + } + @Override public int hashCode() { final int prime = 31; @@ -114,6 +119,10 @@ public class PluginWrapper { void setPluginState(PluginState pluginState) { this.pluginState = pluginState; } + + void setRuntimeMode(RuntimeMode runtimeMode) { + this.runtimeMode = runtimeMode; + } private Plugin createPluginInstance() throws Exception { String pluginClassName = descriptor.getPluginClass(); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java b/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java new file mode 100644 index 0000000..0e7cd3a --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with + * the License. You may obtain a copy of the License in the LICENSE file, or at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package ro.fortsoft.pf4j; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * @author Decebal Suiu + */ +public enum RuntimeMode { + + DEVELOPMENT("development"), // development + DEPLOYMENT("deployment"); // deployment + + private final String name; + + private static final Map map = new HashMap(); + + static { + for (RuntimeMode mode : RuntimeMode.values()) { + map.put(mode.name, mode); + } + } + + private RuntimeMode(final String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public static RuntimeMode byName(String name) { + if (map.containsKey(name)) { + return map.get(name); + } + + throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name + + "'. Must be 'development' or 'deployment'."); + } + +} \ No newline at end of file From 94085d391974d586633cb8821bdaa0f6972c4a16 Mon Sep 17 00:00:00 2001 From: Decebal Suiu Date: Thu, 3 Oct 2013 16:13:12 +0300 Subject: [PATCH 2/3] Update README.md --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cf98f95..018664e 100644 --- a/README.md +++ b/README.md @@ -163,12 +163,15 @@ The main advantage of DEVELOPMENT runtime mode for a plugin developer is that he Lets describe how DEVELOPMENT runtime mode works. -First, you can change the runtime mode using the "pf4j.mode" system property or overriding __DefaultPluginManager.getRuntimeMode()__. +First, you can change the runtime mode using the "pf4j.mode" system property or overriding `DefaultPluginManager.getRuntimeMode()`. For example I run the pf4j demo in eclipse in DEVELOPMENT mode adding only `"-Dpf4j.mode=development"` to the pf4j demo launcher. -You can retrieve the current runtime mode using __PluginManager.getRuntimeMode()__ or in your Plugin implementation with __getWrapper().getRuntimeMode()__(see [WelcomeMessage](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)). -The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is "../plugins", PropertiesPluginDescriptorFinder as PluginDescriptorFinder, DevelopmentPluginClasspath as PluginClassPath). +You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()` or in your Plugin implementation with `getWrapper().getRuntimeMode()`(see [WelcomeMessage](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)). +The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is __"../plugins"__, __PropertiesPluginDescriptorFinder__ as PluginDescriptorFinder, __DevelopmentPluginClasspath__ as PluginClassPath). Another advantage of DEVELOPMENT runtime mode is that you can execute some code lines only in this mode (for example more debug messages). +If you use maven as build manger, after each dependency modification in you plugin (maven module) you must run Maven>Update Project... + + For more details see the demo application. Enable/Disable plugins From cd327605d0fa4a251a867413ffcd376e3823e872 Mon Sep 17 00:00:00 2001 From: Decebal Suiu Date: Thu, 3 Oct 2013 16:15:46 +0300 Subject: [PATCH 3/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 018664e..a7bc2af 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ Lets describe how DEVELOPMENT runtime mode works. First, you can change the runtime mode using the "pf4j.mode" system property or overriding `DefaultPluginManager.getRuntimeMode()`. For example I run the pf4j demo in eclipse in DEVELOPMENT mode adding only `"-Dpf4j.mode=development"` to the pf4j demo launcher. -You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()` or in your Plugin implementation with `getWrapper().getRuntimeMode()`(see [WelcomeMessage](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)). +You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()` or in your Plugin implementation with `getWrapper().getRuntimeMode()`(see [WelcomePlugin](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)). The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is __"../plugins"__, __PropertiesPluginDescriptorFinder__ as PluginDescriptorFinder, __DevelopmentPluginClasspath__ as PluginClassPath). Another advantage of DEVELOPMENT runtime mode is that you can execute some code lines only in this mode (for example more debug messages).