Browse Source

added support for PluginManager.getExtensionClassNames(String pluginId) and simplify enable/disable plugin

pull/9/head
Decebal Suiu 11 years ago
parent
commit
0cf3fc42ff
  1. 17
      demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java
  2. 83
      pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java
  3. 115
      pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
  4. 9
      pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java
  5. 3
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java
  6. 5
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java
  7. 7
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java
  8. 37
      pf4j/src/main/java/ro/fortsoft/pf4j/util/PluginUtils.java

17
demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java

@ -13,11 +13,13 @@
package ro.fortsoft.pf4j.demo;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import ro.fortsoft.pf4j.DefaultPluginManager;
import ro.fortsoft.pf4j.PluginManager;
import ro.fortsoft.pf4j.PluginWrapper;
import ro.fortsoft.pf4j.demo.api.Greeting;
/**
@ -38,18 +40,29 @@ public class Boot {
pluginManager.loadPlugins();
// enable a disabled plugin
pluginManager.enablePlugin("welcome-plugin");
// pluginManager.enablePlugin("welcome-plugin");
// start (active/resolved) the plugins
pluginManager.startPlugins();
// retrieves the extensions for Greeting extension point
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
System.out.println(String.format("### Found %d extensions for the extension point '%s'", greetings.size(), Greeting.class.getName()));
System.out.println(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName()));
for (Greeting greeting : greetings) {
System.out.println(">>> " + greeting.getGreeting());
}
// print extensions for each started plugin
List<PluginWrapper> startedPlugins = pluginManager.getStartedPlugins();
for (PluginWrapper plugin : startedPlugins) {
String pluginId = plugin.getDescriptor().getPluginId();
System.out.println(String.format("Extensions added by plugin '%s':", pluginId));
Set<String> extensionClassNames = pluginManager.getExtensionClassNames(pluginId);
for (String extension : extensionClassNames) {
System.out.println(" " + extension);
}
}
// stop the plugins
pluginManager.stopPlugins();
/*

83
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java

@ -16,12 +16,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,20 +32,15 @@ public class DefaultExtensionFinder implements ExtensionFinder {
private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFinder.class);
private ClassLoader classLoader;
private PluginManager pluginManager;
private ExtensionFactory extensionFactory;
private volatile Set<String> entries;
private volatile Map<String, Set<String>> entries; // cache by pluginId
public DefaultExtensionFinder(ClassLoader classLoader) {
this.classLoader = classLoader;
public DefaultExtensionFinder(PluginManager pluginManager) {
this.pluginManager = pluginManager;
this.extensionFactory = createExtensionFactory();
}
@Override
public void reset() {
entries = null;
}
@Override
public <T> List<ExtensionWrapper<T>> find(Class<T> type) {
log.debug("Checking extension point '{}'", type.getName());
@ -61,14 +51,16 @@ public class DefaultExtensionFinder implements ExtensionFinder {
}
log.debug("Finding extensions for extension point '{}'", type.getName());
readIndexFiles();
List<ExtensionWrapper<T>> result = new ArrayList<ExtensionWrapper<T>>();
if (entries == null) {
entries = readIndexFiles();
}
for (Map.Entry<String, Set<String>> entry : entries.entrySet()) {
String pluginId = entry.getKey();
Set<String> extensionClassNames = entry.getValue();
for (String entry : entries) {
for (String className : extensionClassNames) {
try {
Class<?> extensionType = classLoader.loadClass(entry);
Class<?> extensionType = pluginManager.getPluginClassLoader(pluginId).loadClass(className);
log.debug("Checking extension type '{}'", extensionType.getName());
if (type.isAssignableFrom(extensionType)) {
Object instance = extensionFactory.create(extensionType);
@ -84,6 +76,7 @@ public class DefaultExtensionFinder implements ExtensionFinder {
log.error(e.getMessage(), e);
}
}
}
if (entries.isEmpty()) {
log.debug("No extensions found for extension point '{}'", type.getName());
@ -97,6 +90,17 @@ public class DefaultExtensionFinder implements ExtensionFinder {
return result;
}
@Override
public Set<String> findClassNames(String pluginId) {
return entries.get(pluginId);
}
@Override
public void reset() {
// clear cache
entries = null;
}
/**
* Add the possibility to override the ExtensionFactory.
* The default implementation uses Class.newInstance() method.
@ -122,24 +126,39 @@ public class DefaultExtensionFinder implements ExtensionFinder {
};
}
private Set<String> readIndexFiles() {
log.debug("Reading extensions index files");
Set<String> entries = new HashSet<String>();
private Map<String, Set<String>> readIndexFiles() {
// checking cache
if (entries != null) {
return entries;
}
entries = new HashMap<String, Set<String>>();
List<PluginWrapper> startedPlugins = pluginManager.getStartedPlugins();
for (PluginWrapper plugin : startedPlugins) {
String pluginId = plugin.getDescriptor().getPluginId();
log.debug("Reading extensions index file for plugin '{}'", pluginId);
Set<String> entriesPerPlugin = new HashSet<String>();
try {
Enumeration<URL> indexFiles = classLoader.getResources(ExtensionsIndexer.EXTENSIONS_RESOURCE);
while (indexFiles.hasMoreElements()) {
Reader reader = new InputStreamReader(indexFiles.nextElement().openStream(), "UTF-8");
ExtensionsIndexer.readIndex(reader, entries);
URL url = plugin.getPluginClassLoader().getResource(ExtensionsIndexer.EXTENSIONS_RESOURCE);
log.debug("Read '{}'", url.getFile());
Reader reader = new InputStreamReader(url.openStream(), "UTF-8");
ExtensionsIndexer.readIndex(reader, entriesPerPlugin);
if (entriesPerPlugin.isEmpty()) {
log.debug("No extensions found");
} else {
log.debug("Found possible {} extensions:", entriesPerPlugin.size());
for (String entry : entriesPerPlugin) {
log.debug(" " + entry);
}
}
entries.put(pluginId, entriesPerPlugin);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
if (entries.isEmpty()) {
log.debug("No extensions found");
} else {
log.debug("Found possible {} extensions", entries.size());
}
return entries;

115
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java

@ -12,27 +12,14 @@
*/
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.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.fortsoft.pf4j.util.*;
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;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.*;
/**
* Default implementation of the PluginManager interface.
@ -90,11 +77,6 @@ public class DefaultPluginManager implements PluginManager {
private List<String> enabledPlugins;
private List<String> 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.
@ -179,12 +161,12 @@ public class DefaultPluginManager implements PluginManager {
// TODO uninstalled plugin dependencies?
unresolvedPlugins.remove(pluginWrapper);
resolvedPlugins.add(pluginWrapper);
compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader());
extensionFinder.reset();
return pluginWrapper.getDescriptor().getPluginId();
} catch (PluginException e) {
log.error(e.getMessage(), e);
}
return null;
}
@ -194,9 +176,10 @@ public class DefaultPluginManager implements PluginManager {
@Override
public void startPlugins() {
for (PluginWrapper pluginWrapper : resolvedPlugins) {
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
if (!isPluginDisabled(pluginDescriptor.getPluginId())) {
PluginState pluginState = pluginWrapper.getPluginState();
if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) {
try {
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
pluginWrapper.getPlugin().start();
pluginWrapper.setPluginState(PluginState.STARTED);
@ -219,16 +202,17 @@ public class DefaultPluginManager implements PluginManager {
PluginWrapper pluginWrapper = plugins.get(pluginId);
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
if (pluginWrapper.getPluginState().equals(PluginState.STARTED)) {
if (PluginState.STARTED == pluginWrapper.getPluginState()) {
log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
return PluginState.STARTED;
}
// test for disabled plugin
if (isPluginDisabled(pluginDescriptor.getPluginId())) {
// do nothing
if (PluginState.DISABLED == pluginWrapper.getPluginState()) {
// automatically enable plugin on manual plugin start
if (!enablePlugin(pluginId)) {
return pluginWrapper.getPluginState();
}
}
for (PluginDependency dependency : pluginDescriptor.getDependencies()) {
startPlugin(dependency.getPluginId());
@ -256,9 +240,9 @@ public class DefaultPluginManager implements PluginManager {
Iterator<PluginWrapper> itr = startedPlugins.iterator();
while (itr.hasNext()) {
PluginWrapper pluginWrapper = itr.next();
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
if (!isPluginDisabled(pluginDescriptor.getPluginId())) {
if (PluginState.STARTED == pluginWrapper.getPluginState()) {
try {
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
pluginWrapper.getPlugin().stop();
pluginWrapper.setPluginState(PluginState.STOPPED);
@ -281,13 +265,13 @@ public class DefaultPluginManager implements PluginManager {
PluginWrapper pluginWrapper = plugins.get(pluginId);
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
if (pluginWrapper.getPluginState().equals(PluginState.STOPPED)) {
if (PluginState.STOPPED == pluginWrapper.getPluginState()) {
log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
return PluginState.STOPPED;
}
// test for disabled plugin
if (isPluginDisabled(pluginDescriptor.getPluginId())) {
if (PluginState.DISABLED == pluginWrapper.getPluginState()) {
// do nothing
return pluginWrapper.getPluginState();
}
@ -369,7 +353,7 @@ public class DefaultPluginManager implements PluginManager {
public boolean unloadPlugin(String pluginId) {
try {
PluginState state = stopPlugin(pluginId);
if (!PluginState.STOPPED.equals(state)) {
if (PluginState.STOPPED != state) {
return false;
}
@ -391,13 +375,13 @@ public class DefaultPluginManager implements PluginManager {
// remove the classloader
if (pluginClassLoaders.containsKey(pluginId)) {
PluginClassLoader classLoader = pluginClassLoaders.remove(pluginId);
compoundClassLoader.removeLoader(classLoader);
try {
classLoader.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return true;
} catch (IllegalArgumentException e) {
// ignore not found exceptions because this method is recursive
@ -408,18 +392,34 @@ public class DefaultPluginManager implements PluginManager {
@Override
public boolean disablePlugin(String pluginId) {
if (plugins.containsKey(pluginId)) {
log.debug("Unloading plugin {}", pluginId);
unloadPlugin(pluginId);
if (!plugins.containsKey(pluginId)) {
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
}
PluginWrapper pluginWrapper = plugins.get(pluginId);
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
if (PluginState.DISABLED == getPlugin(pluginId).getPluginState()) {
log.debug("Already disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
return true;
}
if (PluginState.STOPPED == stopPlugin(pluginId)) {
getPlugin(pluginId).setPluginState(PluginState.DISABLED);
extensionFinder.reset();
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;
}
}
log.info("Disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
return true;
}
return false;
@ -427,19 +427,33 @@ public class DefaultPluginManager implements PluginManager {
@Override
public boolean enablePlugin(String pluginId) {
if (!disabledPlugins.remove(pluginId)) {
log.debug("Plugin {} was not disabled", pluginId);
if (!plugins.containsKey(pluginId)) {
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
}
PluginWrapper pluginWrapper = plugins.get(pluginId);
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
if (PluginState.DISABLED != getPlugin(pluginId).getPluginState()) {
log.debug("Plugin plugin '{}:{}' is not disabled", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
return true;
}
try {
if (disabledPlugins.remove(pluginId)) {
FileUtils.writeLines(disabledPlugins, new File(pluginsDirectory, "disabled.txt"));
return true;
}
} catch (IOException e) {
log.error("Failed to enable plugin {}", pluginId, e);
return false;
}
return false;
getPlugin(pluginId).setPluginState(PluginState.CREATED);
extensionFinder.reset();
log.info("Enabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion());
return true;
}
@Override
@ -506,6 +520,11 @@ public class DefaultPluginManager implements PluginManager {
return extensions;
}
@Override
public Set<String> getExtensionClassNames(String pluginId) {
return extensionFinder.findClassNames(pluginId);
}
@Override
public RuntimeMode getRuntimeMode() {
if (runtimeMode == null) {
@ -552,7 +571,7 @@ public class DefaultPluginManager implements PluginManager {
* Add the possibility to override the ExtensionFinder.
*/
protected ExtensionFinder createExtensionFinder() {
return new DefaultExtensionFinder(compoundClassLoader);
return new DefaultExtensionFinder(this);
}
/**
@ -612,7 +631,6 @@ public class DefaultPluginManager implements PluginManager {
resolvedPlugins = new ArrayList<PluginWrapper>();
startedPlugins = new ArrayList<PluginWrapper>();
disabledPlugins = new ArrayList<String>();
compoundClassLoader = new CompoundClassLoader();
pluginClasspath = createPluginClasspath();
pluginDescriptorFinder = createPluginDescriptorFinder();
@ -660,13 +678,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();
@ -717,7 +735,6 @@ public class DefaultPluginManager implements PluginManager {
resolvedPlugins = dependencyResolver.getSortedPlugins();
for (PluginWrapper pluginWrapper : resolvedPlugins) {
unresolvedPlugins.remove(pluginWrapper);
compoundClassLoader.addLoader(pluginWrapper.getPluginClassLoader());
log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId());
}
}

9
pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java

@ -13,14 +13,23 @@
package ro.fortsoft.pf4j;
import java.util.List;
import java.util.Set;
/**
* @author Decebal Suiu
*/
public interface ExtensionFinder {
/**
* Retrieves a list with all extensions found for an extension point.
*/
public <T> List<ExtensionWrapper<T>> find(Class<T> type);
/**
* Retrieves a list with all extension class names found for a plugin.
*/
public Set<String> findClassNames(String pluginId);
public void reset();
}

3
pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java

@ -14,6 +14,7 @@ package ro.fortsoft.pf4j;
import java.io.File;
import java.util.List;
import java.util.Set;
/**
* Provides the functionality for plugin management such as load,
@ -123,6 +124,8 @@ public interface PluginManager {
public <T> List<T> getExtensions(Class<T> type);
public Set<String> getExtensionClassNames(String pluginId);
/**
* The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT.
*/

5
pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java

@ -18,12 +18,9 @@ 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 DISABLED = new PluginState("DISABLED");
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");
private String status;

7
pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java

@ -79,6 +79,13 @@ public class PluginWrapper {
return runtimeMode;
}
/**
* Shortcut
*/
public String getPluginId() {
return getDescriptor().getPluginId();
}
@Override
public int hashCode() {
final int prime = 31;

37
pf4j/src/main/java/ro/fortsoft/pf4j/util/PluginUtils.java

@ -0,0 +1,37 @@
/*
* 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.util;
import ro.fortsoft.pf4j.PluginManager;
import ro.fortsoft.pf4j.PluginState;
import ro.fortsoft.pf4j.PluginWrapper;
import java.util.List;
/**
* @author Decebal Suiu
*/
public class PluginUtils {
public static boolean isPluginStarted(PluginManager pluginManager, String pluginId) {
List<PluginWrapper> startedPlugins = pluginManager.getStartedPlugins();
for (PluginWrapper plugin: startedPlugins) {
if (pluginId.equals(plugin.getPluginId())) {
return PluginState.STARTED == plugin.getPluginState();
}
}
return false;
}
}
Loading…
Cancel
Save