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 8dde182..548ef4f 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,11 +1,11 @@ /* * Copyright 2012 Decebal Suiu - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. @@ -26,24 +26,30 @@ import ro.fortsoft.pf4j.demo.api.Greeting; * @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 + + // load the plugins pluginManager.loadPlugins(); + + // enable a disabled plugin + pluginManager.enablePlugin("welcome-plugin"); + + // start (active/resolved) the plugins pluginManager.startPlugins(); - - // retrieves the extensions for Greeting extension point + + // retrieves the extensions for Greeting extension point List greetings = pluginManager.getExtensions(Greeting.class); + System.out.println(String.format("### Found %d extensions for the extension point '%s'", greetings.size(), Greeting.class.getName())); for (Greeting greeting : greetings) { System.out.println(">>> " + greeting.getGreeting()); } - + // stop the plugins pluginManager.stopPlugins(); /* @@ -53,15 +59,15 @@ public class Boot { 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)); + System.out.println(StringUtils.repeat("#", 40)); } - + } diff --git a/demo/disabled.txt b/demo/disabled.txt deleted file mode 100644 index b8fedab..0000000 --- a/demo/disabled.txt +++ /dev/null @@ -1,6 +0,0 @@ -######################################## -# - load all plugins except these -# - add one plugin id on each line -# - put this file in plugins folder -######################################## -welcome-plugin diff --git a/demo/enabled.txt b/demo/enabled.txt deleted file mode 100644 index 96a92b3..0000000 --- a/demo/enabled.txt +++ /dev/null @@ -1,6 +0,0 @@ -######################################## -# - load only these plugins -# - add one plugin id on each line -# - put this file in plugins folder -######################################## -welcome-plugin diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index 557bb77..e9d46a5 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -127,6 +127,18 @@ public class DefaultPluginManager implements PluginManager { return new ArrayList(plugins.values()); } + @Override + public List getPlugins(PluginState pluginState) { + List plugins= new ArrayList(); + for (PluginWrapper plugin : getPlugins()) { + if (pluginState.equals(plugin.getPluginState())) { + plugins.add(plugin); + } + } + + return plugins; + } + @Override public List getResolvedPlugins() { return resolvedPlugins; @@ -182,14 +194,17 @@ public class DefaultPluginManager implements PluginManager { @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); - } + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + if (!isPluginDisabled(pluginDescriptor.getPluginId())) { + try { + log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + startedPlugins.add(pluginWrapper); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } } } @@ -201,15 +216,24 @@ public class DefaultPluginManager implements PluginManager { if (!plugins.containsKey(pluginId)) { throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); } + PluginWrapper pluginWrapper = plugins.get(pluginId); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); if (pluginWrapper.getPluginState().equals(PluginState.STARTED)) { log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); return PluginState.STARTED; } - for (PluginDependency dependency : pluginDescriptor.getDependencies()) { + + // test for disabled plugin + if (isPluginDisabled(pluginDescriptor.getPluginId())) { + // do nothing + return pluginWrapper.getPluginState(); + } + + for (PluginDependency dependency : pluginDescriptor.getDependencies()) { startPlugin(dependency.getPluginId()); } + try { log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); pluginWrapper.getPlugin().start(); @@ -218,6 +242,7 @@ public class DefaultPluginManager implements PluginManager { } catch (PluginException e) { log.error(e.getMessage(), e); } + return pluginWrapper.getPluginState(); } @@ -232,14 +257,16 @@ public class DefaultPluginManager implements PluginManager { while (itr.hasNext()) { PluginWrapper pluginWrapper = itr.next(); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - try { - log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - itr.remove(); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } + if (!isPluginDisabled(pluginDescriptor.getPluginId())) { + try { + log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); + pluginWrapper.getPlugin().stop(); + pluginWrapper.setPluginState(PluginState.STOPPED); + itr.remove(); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } } } @@ -251,15 +278,24 @@ public class DefaultPluginManager implements PluginManager { if (!plugins.containsKey(pluginId)) { throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); } + PluginWrapper pluginWrapper = plugins.get(pluginId); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); if (pluginWrapper.getPluginState().equals(PluginState.STOPPED)) { log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); return PluginState.STOPPED; } + + // test for disabled plugin + if (isPluginDisabled(pluginDescriptor.getPluginId())) { + // do nothing + return pluginWrapper.getPluginState(); + } + for (PluginDependency dependency : pluginDescriptor.getDependencies()) { stopPlugin(dependency.getPluginId()); } + try { log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); pluginWrapper.getPlugin().stop(); @@ -268,6 +304,7 @@ public class DefaultPluginManager implements PluginManager { } catch (PluginException e) { log.error(e.getMessage(), e); } + return pluginWrapper.getPluginState(); } @@ -365,9 +402,46 @@ public class DefaultPluginManager implements PluginManager { } catch (IllegalArgumentException e) { // ignore not found exceptions because this method is recursive } + return false; } + @Override + public boolean disablePlugin(String pluginId) { + if (plugins.containsKey(pluginId)) { + log.debug("Unloading plugin {}", pluginId); + unloadPlugin(pluginId); + } + + if (disabledPlugins.add(pluginId)) { + try { + FileUtils.writeLines(disabledPlugins, new File(pluginsDirectory, "disabled.txt")); + return true; + } catch (IOException e) { + log.error("Failed to disable plugin {}", pluginId, e); + } + } + + return false; + } + + @Override + public boolean enablePlugin(String pluginId) { + if (!disabledPlugins.remove(pluginId)) { + log.debug("Plugin {} was not disabled", pluginId); + return true; + } + + try { + FileUtils.writeLines(disabledPlugins, new File(pluginsDirectory, "disabled.txt")); + return true; + } catch (IOException e) { + log.error("Failed to enable plugin {}", pluginId, e); + } + + return false; + } + @Override public boolean deletePlugin(String pluginId) { if (!plugins.containsKey(pluginId)) { @@ -495,7 +569,8 @@ public class DefaultPluginManager implements PluginManager { return new PluginClasspath(); } - protected boolean isPluginDisabled(String pluginId) { + @Override + public boolean isPluginDisabled(String pluginId) { if (enabledPlugins.isEmpty()) { return disabledPlugins.contains(pluginId); } @@ -575,12 +650,6 @@ public class DefaultPluginManager implements PluginManager { String pluginClassName = pluginDescriptor.getPluginClass(); log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); - // test for disabled plugin - if (isPluginDisabled(pluginDescriptor.getPluginId())) { - log.info("Plugin '{}' is disabled", pluginPath); - return null; - } - // load plugin log.debug("Loading plugin '{}'", pluginPath); PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory, pluginClasspath); @@ -591,6 +660,13 @@ public class DefaultPluginManager implements PluginManager { log.debug("Creating wrapper for plugin '{}'", pluginPath); PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor, pluginPath, pluginLoader.getPluginClassLoader()); pluginWrapper.setRuntimeMode(getRuntimeMode()); + /* + // test for disabled plugin + if (isPluginDisabled(pluginDescriptor.getPluginId())) { + log.info("Plugin '{}' is disabled", pluginPath); + pluginWrapper.setPluginState(PluginState.DISABLED); + } + */ log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); String pluginId = pluginDescriptor.getPluginId(); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java index 60c79ca..0252d8f 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java @@ -1,24 +1,27 @@ /* * 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.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.util.Collections; +import java.util.Enumeration; import java.util.List; /** * One instance of this class should be created by plugin manager for every available plug-in. - * + * * @author Decebal Suiu */ public class PluginClassLoader extends URLClassLoader { @@ -29,10 +32,10 @@ public class PluginClassLoader extends URLClassLoader { private PluginManager pluginManager; private PluginDescriptor pluginDescriptor; - + public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) { super(new URL[0], parent); - + this.pluginManager = pluginManager; this.pluginDescriptor = pluginDescriptor; } @@ -91,4 +94,21 @@ public class PluginClassLoader extends URLClassLoader { return super.loadClass(className); } + @Override + public URL getResource(String name) { + if (pluginManager.isPluginDisabled(pluginDescriptor.getPluginId())) { + return null; + } + + return super.getResource(name); + } + + @Override + public Enumeration getResources(String name) throws IOException { + if (pluginManager.isPluginDisabled(pluginDescriptor.getPluginId())) { + return Collections.emptyEnumeration(); + } + + return super.getResources(name); + } } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java index 5fbe975..541bb0a 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java @@ -1,11 +1,11 @@ /* * Copyright 2013 Decebal Suiu - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. @@ -16,25 +16,25 @@ 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". - * + * The classpath of the plugin after it was unpacked. + * It contains classes directories and lib directories (directories that contains jars). + * All directories are relative to plugin repository. + * The default values are "classes" and "lib". + * * @author Decebal Suiu */ public class PluginClasspath { private static final String DEFAULT_CLASSES_DIRECTORY = "classes"; private static final String DEFAULT_LIB_DIRECTORY = "lib"; - + protected List classesDirectories; protected List libDirectories; - + public PluginClasspath() { classesDirectories = new ArrayList(); libDirectories = new ArrayList(); - + addResources(); } @@ -53,10 +53,10 @@ public class PluginClasspath { public void setLibDirectories(List libDirectories) { this.libDirectories = libDirectories; } - + protected void addResources() { classesDirectories.add(DEFAULT_CLASSES_DIRECTORY); - libDirectories.add(DEFAULT_LIB_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 ec95c4e..4173c2b 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java @@ -28,15 +28,20 @@ public interface PluginManager { */ public List getPlugins(); + /** + * Retrieves all plugins with this state. + */ + public List getPlugins(PluginState pluginState); + /** * Retrieves all resolved plugins (with resolved dependency). */ - public List getResolvedPlugins(); + public List getResolvedPlugins(); /** * Retrieves all unresolved plugins (with unresolved dependency). */ - public List getUnresolvedPlugins(); + public List getUnresolvedPlugins(); /** * Retrieves all started plugins. @@ -88,6 +93,24 @@ public interface PluginManager { */ public boolean unloadPlugin(String pluginId); + public boolean isPluginDisabled(String pluginId); + + /** + * Disables a plugin from being loaded. + * + * @param pluginId + * @return true if plugin is disabled + */ + public boolean disablePlugin(String pluginId); + + /** + * Enables a plugin that has previously been disabled. + * + * @param pluginId + * @return true if plugin is enabled + */ + public boolean enablePlugin(String pluginId); + /** * Deletes a plugin. * diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java index 3727087..06d7815 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java @@ -1,11 +1,11 @@ /* * Copyright 2012 Decebal Suiu - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. @@ -18,19 +18,37 @@ package ro.fortsoft.pf4j; public class PluginState { public static final PluginState CREATED = new PluginState("CREATED"); - public static final PluginState INITIALIZED = new PluginState("INITIALIZED"); +// public static final PluginState INITIALIZED = new PluginState("INITIALIZED"); +// public static final PluginState DISABLED = new PluginState("DISABLED"); public static final PluginState STARTED = new PluginState("STARTED"); public static final PluginState STOPPED = new PluginState("STOPPED"); - public static final PluginState DESTROYED = new PluginState("DESTROYED"); - public static final PluginState FAILED = new PluginState("FAILED"); +// public static final PluginState DESTROYED = new PluginState("DESTROYED"); +// public static final PluginState FAILED = new PluginState("FAILED"); private String status; - + private PluginState(String status) { this.status = status; } - @Override + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PluginState that = (PluginState) o; + + if (!status.equals(that.status)) return false; + + return true; + } + + @Override + public int hashCode() { + return status.hashCode(); + } + + @Override public String toString() { return status; } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java index 68cc2b3..2ac97db 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java @@ -12,11 +12,9 @@ */ package ro.fortsoft.pf4j.util; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; +import java.io.*; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -50,6 +48,21 @@ public class FileUtils { return lines; } + public static void writeLines(Collection lines, File file) throws IOException { + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file)); + for (String line : lines) { + writer.write(line); + writer.write('\n'); + } + } finally { + if (writer != null) { + writer.close(); + } + } + } + /** * Delete a file or recursively delete a folder. * @@ -71,6 +84,7 @@ public class FileUtils { } } success |= fileOrFolder.delete(); + return success; } }