From: Decebal Suiu Date: Wed, 9 Apr 2014 14:33:58 +0000 (+0300) Subject: add listeners to plugin state changes X-Git-Tag: release-0.7.0~2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7e00f212b15a07584bf0130272761675302c1116;p=pf4j.git add listeners to plugin state changes --- diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java index c31f5d3..295d5c8 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java @@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory; * * @author Decebal Suiu */ -public class DefaultExtensionFinder implements ExtensionFinder { +public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListener { private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFinder.class); @@ -95,8 +95,8 @@ public class DefaultExtensionFinder implements ExtensionFinder { return entries.get(pluginId); } - @Override - public void reset() { + public void pluginStateChanged(PluginStateEvent event) { + // TODO optimize (do only for some transitions) // clear cache entries = null; } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java index cf02045..47dce71 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java @@ -77,6 +77,11 @@ public class DefaultPluginManager implements PluginManager { private List enabledPlugins; private List disabledPlugins; + /** + * The registered {@link PluginStateListener}s. + */ + private List pluginStateListeners; + /** * Cache value for the runtime mode. No need to re-read it because it wont change at * runtime. @@ -143,7 +148,7 @@ public class DefaultPluginManager implements PluginManager { @Override public String loadPlugin(File pluginArchiveFile) { - if (pluginArchiveFile == null || !pluginArchiveFile.exists()) { + if ((pluginArchiveFile == null) || !pluginArchiveFile.exists()) { throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginArchiveFile)); } @@ -153,7 +158,7 @@ public class DefaultPluginManager implements PluginManager { } catch (IOException e) { log.error(e.getMessage(), e); } - if (pluginDirectory == null || !pluginDirectory.exists()) { + if ((pluginDirectory == null) || !pluginDirectory.exists()) { throw new IllegalArgumentException(String.format("Failed to expand %s", pluginArchiveFile)); } @@ -162,7 +167,9 @@ public class DefaultPluginManager implements PluginManager { // TODO uninstalled plugin dependencies? unresolvedPlugins.remove(pluginWrapper); resolvedPlugins.add(pluginWrapper); - extensionFinder.reset(); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, null)); + return pluginWrapper.getDescriptor().getPluginId(); } catch (PluginException e) { log.error(e.getMessage(), e); @@ -185,6 +192,8 @@ public class DefaultPluginManager implements PluginManager { pluginWrapper.getPlugin().start(); pluginWrapper.setPluginState(PluginState.STARTED); startedPlugins.add(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); } catch (PluginException e) { log.error(e.getMessage(), e); } @@ -201,17 +210,18 @@ public class DefaultPluginManager implements PluginManager { throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); } - PluginWrapper pluginWrapper = plugins.get(pluginId); + PluginWrapper pluginWrapper = getPlugin(pluginId); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - if (PluginState.STARTED == pluginWrapper.getPluginState()) { + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STARTED == pluginState) { log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); return PluginState.STARTED; } - if (PluginState.DISABLED == pluginWrapper.getPluginState()) { + if (PluginState.DISABLED == pluginState) { // automatically enable plugin on manual plugin start if (!enablePlugin(pluginId)) { - return pluginWrapper.getPluginState(); + return pluginState; } } @@ -224,7 +234,9 @@ public class DefaultPluginManager implements PluginManager { pluginWrapper.getPlugin().start(); pluginWrapper.setPluginState(PluginState.STARTED); startedPlugins.add(pluginWrapper); - } catch (PluginException e) { + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { log.error(e.getMessage(), e); } @@ -241,13 +253,16 @@ public class DefaultPluginManager implements PluginManager { Iterator itr = startedPlugins.iterator(); while (itr.hasNext()) { PluginWrapper pluginWrapper = itr.next(); - if (PluginState.STARTED == pluginWrapper.getPluginState()) { + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STARTED == pluginState) { try { PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); pluginWrapper.getPlugin().stop(); pluginWrapper.setPluginState(PluginState.STOPPED); itr.remove(); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); } catch (PluginException e) { log.error(e.getMessage(), e); } @@ -264,17 +279,18 @@ public class DefaultPluginManager implements PluginManager { throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); } - PluginWrapper pluginWrapper = plugins.get(pluginId); + PluginWrapper pluginWrapper = getPlugin(pluginId); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - if (PluginState.STOPPED == pluginWrapper.getPluginState()) { + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STOPPED == pluginState) { log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); return PluginState.STOPPED; } // test for disabled plugin - if (PluginState.DISABLED == pluginWrapper.getPluginState()) { + if (PluginState.DISABLED == pluginState) { // do nothing - return pluginWrapper.getPluginState(); + return pluginState; } for (PluginDependency dependency : pluginDescriptor.getDependencies()) { @@ -286,6 +302,8 @@ public class DefaultPluginManager implements PluginManager { pluginWrapper.getPlugin().stop(); pluginWrapper.setPluginState(PluginState.STOPPED); startedPlugins.remove(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); } catch (PluginException e) { log.error(e.getMessage(), e); } @@ -353,12 +371,12 @@ public class DefaultPluginManager implements PluginManager { @Override public boolean unloadPlugin(String pluginId) { try { - PluginState state = stopPlugin(pluginId); - if (PluginState.STOPPED != state) { + PluginState pluginState = stopPlugin(pluginId); + if (PluginState.STOPPED != pluginState) { return false; } - PluginWrapper pluginWrapper = plugins.get(pluginId); + PluginWrapper pluginWrapper = getPlugin(pluginId); PluginDescriptor descriptor = pluginWrapper.getDescriptor(); List dependencies = descriptor.getDependencies(); for (PluginDependency dependency : dependencies) { @@ -371,7 +389,8 @@ public class DefaultPluginManager implements PluginManager { plugins.remove(pluginId); resolvedPlugins.remove(pluginWrapper); pathToIdMap.remove(pluginWrapper.getPluginPath()); - extensionFinder.reset(); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); // remove the classloader if (pluginClassLoaders.containsKey(pluginId)) { @@ -398,17 +417,18 @@ public class DefaultPluginManager implements PluginManager { } - PluginWrapper pluginWrapper = plugins.get(pluginId); + PluginWrapper pluginWrapper = getPlugin(pluginId); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - - if (PluginState.DISABLED == getPlugin(pluginId).getPluginState()) { + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.DISABLED == pluginState) { log.debug("Already disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); return true; } if (PluginState.STOPPED == stopPlugin(pluginId)) { - getPlugin(pluginId).setPluginState(PluginState.DISABLED); - extensionFinder.reset(); + pluginWrapper.setPluginState(PluginState.DISABLED); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); if (disabledPlugins.add(pluginId)) { try { @@ -432,10 +452,10 @@ public class DefaultPluginManager implements PluginManager { throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); } - PluginWrapper pluginWrapper = plugins.get(pluginId); + PluginWrapper pluginWrapper = getPlugin(pluginId); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - - if (PluginState.DISABLED != getPlugin(pluginId).getPluginState()) { + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.DISABLED != pluginState) { log.debug("Plugin plugin '{}:{}' is not disabled", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); return true; } @@ -449,8 +469,9 @@ public class DefaultPluginManager implements PluginManager { return false; } - getPlugin(pluginId).setPluginState(PluginState.CREATED); - extensionFinder.reset(); + pluginWrapper.setPluginState(PluginState.CREATED); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); log.info("Enabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); @@ -462,10 +483,10 @@ public class DefaultPluginManager implements PluginManager { if (!plugins.containsKey(pluginId)) { throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); } - PluginWrapper pw = getPlugin(pluginId); - PluginState state = stopPlugin(pluginId); - if (PluginState.STOPPED != state) { + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginState pluginState = stopPlugin(pluginId); + if (PluginState.STOPPED != pluginState) { log.error("Failed to stop plugin {} on delete", pluginId); return false; } @@ -475,14 +496,14 @@ public class DefaultPluginManager implements PluginManager { return false; } - File pluginFolder = new File(pluginsDirectory, pw.getPluginPath()); + File pluginFolder = new File(pluginsDirectory, pluginWrapper.getPluginPath()); File pluginZip = null; FileFilter zipFilter = new ZipFileFilter(); File[] zipFiles = pluginsDirectory.listFiles(zipFilter); if (zipFiles != null) { // strip prepended / from the plugin path - String dirName = pw.getPluginPath().substring(1); + String dirName = pluginWrapper.getPluginPath().substring(1); // find the zip file that matches the plugin path for (File zipFile : zipFiles) { String name = zipFile.getName().substring(0, zipFile.getName().lastIndexOf('.')); @@ -499,6 +520,7 @@ public class DefaultPluginManager implements PluginManager { if (pluginZip != null && pluginZip.exists()) { FileUtils.delete(pluginZip); } + return true; } @@ -551,10 +573,21 @@ public class DefaultPluginManager implements PluginManager { } } log.warn("Failed to find the plugin for {}", clazz); + return null; } - /** + @Override + public synchronized void addPluginStateListener(PluginStateListener listener) { + pluginStateListeners.add(listener); + } + + @Override + public synchronized void removePluginStateListener(PluginStateListener listener) { + pluginStateListeners.remove(listener); + } + + /** * Add the possibility to override the PluginDescriptorFinder. * By default if getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a * PropertiesPluginDescriptorFinder is returned else this method returns @@ -572,7 +605,10 @@ public class DefaultPluginManager implements PluginManager { * Add the possibility to override the ExtensionFinder. */ protected ExtensionFinder createExtensionFinder() { - return new DefaultExtensionFinder(this); + DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); + addPluginStateListener(extensionFinder); + + return extensionFinder; } /** @@ -632,6 +668,8 @@ public class DefaultPluginManager implements PluginManager { startedPlugins = new ArrayList(); disabledPlugins = new ArrayList(); + pluginStateListeners = new ArrayList(); + pluginClasspath = createPluginClasspath(); pluginDescriptorFinder = createPluginDescriptorFinder(); extensionFinder = createExtensionFinder(); @@ -739,4 +777,11 @@ public class DefaultPluginManager implements PluginManager { } } + private synchronized void firePluginStateEvent(PluginStateEvent event) { + for (PluginStateListener listener : pluginStateListeners) { + log.debug("Fire '{}' to '{}'", event, listener); + listener.pluginStateChanged(event); + } + } + } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java index c518566..7236b49 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java @@ -30,6 +30,4 @@ public interface ExtensionFinder { */ public Set findClassNames(String pluginId); - public void reset(); - } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java index 8bd2daa..03e7683 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.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. @@ -46,10 +46,11 @@ class PluginLoader { public PluginLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, File pluginRepository, PluginClasspath pluginClasspath) { this.pluginRepository = pluginRepository; - this.pluginClasspath = pluginClasspath; - ClassLoader parent = getClass().getClassLoader(); - pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, parent); - log.debug("Created class loader {}", pluginClassLoader); + this.pluginClasspath = pluginClasspath; + + ClassLoader parent = getClass().getClassLoader(); + pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, parent); + log.debug("Created class loader '{}'", pluginClassLoader); } public File getPluginRepository() { @@ -70,15 +71,15 @@ class PluginLoader { private boolean loadClasses() { List classesDirectories = pluginClasspath.getClassesDirectories(); - + // add each classes directory to plugin class loader for (String classesDirectory : classesDirectories) { // make 'classesDirectory' absolute File file = new File(pluginRepository, classesDirectory).getAbsoluteFile(); - + if (file.exists() && file.isDirectory()) { log.debug("Found '{}' directory", file.getPath()); - + try { pluginClassLoader.addURL(file.toURI().toURL()); log.debug("Added '{}' to the class loader path", file); @@ -98,12 +99,12 @@ class PluginLoader { */ private boolean loadJars() { List libDirectories = pluginClasspath.getLibDirectories(); - + // add each jars directory to plugin class loader for (String libDirectory : libDirectories) { // make 'libDirectory' absolute File file = new File(pluginRepository, libDirectory).getAbsoluteFile(); - + // collect all jars from current lib directory in jars variable Vector jars = new Vector(); getJars(jars, file); diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java index f656313..daf6fbf 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java @@ -137,4 +137,8 @@ public interface PluginManager { */ public RuntimeMode getRuntimeMode(); + public void addPluginStateListener(PluginStateListener listener); + + public void removePluginStateListener(PluginStateListener listener); + } diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java new file mode 100644 index 0000000..bea202e --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014 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.EventObject; + +/** + * @author Decebal Suiu + */ +public class PluginStateEvent extends EventObject { + + private PluginWrapper plugin; + private PluginState oldState; + + public PluginStateEvent(PluginManager source, PluginWrapper plugin, PluginState oldState) { + super(source); + + this.plugin = plugin; + this.oldState = oldState; + } + + @Override + public PluginManager getSource() { + return (PluginManager) super.getSource(); + } + + public PluginWrapper getPlugin() { + return plugin; + } + + public PluginState getPluginState() { + return plugin.getPluginState(); + } + + public PluginState getOldState() { + return oldState; + } + + @Override + public String toString() { + return "PluginStateEvent [plugin=" + plugin.getPluginId() + + ", newState=" + getPluginState() + + ", oldState=" + oldState + + ']'; + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java new file mode 100644 index 0000000..4af2e91 --- /dev/null +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java @@ -0,0 +1,29 @@ +/* + * Copyright 2014 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.EventListener; + +/** + * PluginStateListener defines the interface for an object that listens to plugin state changes. + * + * @author Decebal Suiu + */ +public interface PluginStateListener extends EventListener { + + /** + * Invoked when a plugin's state (for example DISABLED, STARTED) is changed. + */ + public void pluginStateChanged(PluginStateEvent event); + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java index b2ab37d..da9908a 100644 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java +++ b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java @@ -119,8 +119,7 @@ public class PluginWrapper { @Override public String toString() { - return "PluginWrapper [descriptor=" + descriptor + ", pluginPath=" - + pluginPath + "]"; + return "PluginWrapper [descriptor=" + descriptor + ", pluginPath=" + pluginPath + "]"; } void setPluginState(PluginState pluginState) {