@@ -20,12 +20,6 @@ Artifacts | |||
Using Maven | |||
------------------- | |||
First you must install the pf4j artifacts in your local maven repository with: | |||
mvn clean install | |||
I will upload these artifacts in maven central repository as soon as possible. | |||
In your pom.xml you must define the dependencies to PF4J artifacts with: | |||
```xml | |||
@@ -43,8 +37,8 @@ How to use | |||
It's very simple to add pf4j in your application: | |||
public static void main(String[] args) { | |||
... | |||
... | |||
PluginManager pluginManager = new DefaultPluginManager(); | |||
pluginManager.loadPlugins(); | |||
pluginManager.startPlugins(); | |||
@@ -118,11 +112,15 @@ In above code I supply an extension for the `Greeting` extension point. | |||
You can retrieve all extensions for an extension point with: | |||
List<ExtensionWrapper<Greeting>> greetings = pluginManager.getExtensions(Greeting.class); | |||
for (ExtensionWrapper<Greeting> greeting : greetings) { | |||
System.out.println(">>> " + greeting.getInstance().getGreeting()); | |||
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class); | |||
for (Greeting greeting : greetings) { | |||
System.out.println(">>> " + greeting.getGreeting()); | |||
} | |||
The output is: | |||
>>> Welcome | |||
>>> Hello | |||
For more information please see the demo sources. | |||
@@ -17,7 +17,6 @@ import java.util.List; | |||
import org.apache.commons.lang.StringUtils; | |||
import ro.fortsoft.pf4j.DefaultPluginManager; | |||
import ro.fortsoft.pf4j.ExtensionWrapper; | |||
import ro.fortsoft.pf4j.PluginManager; | |||
import ro.fortsoft.pf4j.demo.api.Greeting; | |||
@@ -37,9 +36,9 @@ public class Boot { | |||
pluginManager.loadPlugins(); | |||
pluginManager.startPlugins(); | |||
List<ExtensionWrapper<Greeting>> greetings = pluginManager.getExtensions(Greeting.class); | |||
for (ExtensionWrapper<Greeting> greeting : greetings) { | |||
System.out.println(">>> " + greeting.getInstance().getGreeting()); | |||
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class); | |||
for (Greeting greeting : greetings) { | |||
System.out.println(">>> " + greeting.getGreeting()); | |||
} | |||
pluginManager.stopPlugins(); |
@@ -0,0 +1,28 @@ | |||
/* | |||
* 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; | |||
/** | |||
* CyclicDependencyException will be thrown if a cyclic dependency is detected. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
class CyclicDependencyException extends PluginException { | |||
private static final long serialVersionUID = 1L; | |||
public CyclicDependencyException(String message) { | |||
super(message); | |||
} | |||
} |
@@ -15,8 +15,6 @@ package ro.fortsoft.pf4j; | |||
import java.io.File; | |||
import java.io.FilenameFilter; | |||
import java.io.IOException; | |||
import java.lang.reflect.Constructor; | |||
import java.lang.reflect.Modifier; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
@@ -30,7 +28,6 @@ import ro.fortsoft.pf4j.util.UberClassLoader; | |||
import ro.fortsoft.pf4j.util.Unzip; | |||
import ro.fortsoft.pf4j.util.ZipFilter; | |||
/** | |||
* Default implementation of the PluginManager interface. | |||
* | |||
@@ -52,7 +49,7 @@ public class DefaultPluginManager implements PluginManager { | |||
/** | |||
* A map of plugins this manager is responsible for (the key is the 'pluginId'). | |||
*/ | |||
private Map<String, Plugin> plugins; | |||
private Map<String, PluginWrapper> plugins; | |||
/** | |||
* A map of plugin class loaders (he key is the 'pluginId'). | |||
@@ -67,17 +64,17 @@ public class DefaultPluginManager implements PluginManager { | |||
/** | |||
* A list with unresolved plugins (unresolved dependency). | |||
*/ | |||
private List<Plugin> unresolvedPlugins; | |||
private List<PluginWrapper> unresolvedPlugins; | |||
/** | |||
* A list with resolved plugins (resolved dependency). | |||
*/ | |||
private List<Plugin> resolvedPlugins; | |||
private List<PluginWrapper> resolvedPlugins; | |||
/** | |||
* A list with disabled plugins. | |||
*/ | |||
private List<Plugin> disabledPlugins; | |||
private List<PluginWrapper> disabledPlugins; | |||
private UberClassLoader uberClassLoader; | |||
@@ -96,12 +93,12 @@ public class DefaultPluginManager implements PluginManager { | |||
*/ | |||
public DefaultPluginManager(File pluginsDirectory) { | |||
this.pluginsDirectory = pluginsDirectory; | |||
plugins = new HashMap<String, Plugin>(); | |||
plugins = new HashMap<String, PluginWrapper>(); | |||
pluginClassLoaders = new HashMap<String, PluginClassLoader>(); | |||
pathToIdMap = new HashMap<String, String>(); | |||
unresolvedPlugins = new ArrayList<Plugin>(); | |||
resolvedPlugins = new ArrayList<Plugin>(); | |||
disabledPlugins = new ArrayList<Plugin>(); | |||
unresolvedPlugins = new ArrayList<PluginWrapper>(); | |||
resolvedPlugins = new ArrayList<PluginWrapper>(); | |||
disabledPlugins = new ArrayList<PluginWrapper>(); | |||
pluginDescriptorFinder = new DefaultPluginDescriptorFinder(); | |||
uberClassLoader = new UberClassLoader(); | |||
extensionFinder = new DefaultExtensionFinder(uberClassLoader); | |||
@@ -110,23 +107,23 @@ public class DefaultPluginManager implements PluginManager { | |||
/** | |||
* Retrieves all active plugins. | |||
*/ | |||
public List<Plugin> getPlugins() { | |||
return new ArrayList<Plugin>(plugins.values()); | |||
public List<PluginWrapper> getPlugins() { | |||
return new ArrayList<PluginWrapper>(plugins.values()); | |||
} | |||
public List<Plugin> getResolvedPlugins() { | |||
public List<PluginWrapper> getResolvedPlugins() { | |||
return resolvedPlugins; | |||
} | |||
public Plugin getPlugin(String pluginId) { | |||
public PluginWrapper getPlugin(String pluginId) { | |||
return plugins.get(pluginId); | |||
} | |||
public List<Plugin> getUnresolvedPlugins() { | |||
public List<PluginWrapper> getUnresolvedPlugins() { | |||
return unresolvedPlugins; | |||
} | |||
public List<Plugin> getDisabledPlugins() { | |||
public List<PluginWrapper> getDisabledPlugins() { | |||
return disabledPlugins; | |||
} | |||
@@ -134,13 +131,13 @@ public class DefaultPluginManager implements PluginManager { | |||
* Start all active plugins. | |||
*/ | |||
public void startPlugins() { | |||
List<Plugin> resolvedPlugins = getResolvedPlugins(); | |||
for (Plugin plugin : resolvedPlugins) { | |||
List<PluginWrapper> resolvedPlugins = getResolvedPlugins(); | |||
for (PluginWrapper pluginWrapper : resolvedPlugins) { | |||
try { | |||
plugin.start(); | |||
LOG.info("Start plugin '" + pluginWrapper.getDescriptor().getPluginId() + "'"); | |||
pluginWrapper.getPlugin().start(); | |||
} catch (PluginException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
LOG.error(e.getMessage(), e); | |||
} | |||
} | |||
} | |||
@@ -149,13 +146,13 @@ public class DefaultPluginManager implements PluginManager { | |||
* Stop all active plugins. | |||
*/ | |||
public void stopPlugins() { | |||
List<Plugin> resolvedPlugins = getResolvedPlugins(); | |||
for (Plugin plugin : resolvedPlugins) { | |||
List<PluginWrapper> resolvedPlugins = getResolvedPlugins(); | |||
for (PluginWrapper pluginWrapper : resolvedPlugins) { | |||
try { | |||
plugin.stop(); | |||
LOG.info("Stop plugin '" + pluginWrapper.getDescriptor().getPluginId() + "'"); | |||
pluginWrapper.getPlugin().stop(); | |||
} catch (PluginException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
LOG.error(e.getMessage(), e); | |||
} | |||
} | |||
} | |||
@@ -178,7 +175,6 @@ public class DefaultPluginManager implements PluginManager { | |||
expandPluginArchive(zipFile); | |||
} catch (IOException e) { | |||
LOG.error(e.getMessage(), e); | |||
e.printStackTrace(); | |||
} | |||
} | |||
@@ -188,9 +184,8 @@ public class DefaultPluginManager implements PluginManager { | |||
for (String directory : directories) { | |||
try { | |||
loadPlugin(directory); | |||
} catch (Exception e) { | |||
} catch (PluginException e) { | |||
LOG.error(e.getMessage(), e); | |||
e.printStackTrace(); | |||
} | |||
} | |||
@@ -201,7 +196,11 @@ public class DefaultPluginManager implements PluginManager { | |||
} | |||
// resolve 'unresolvedPlugins' | |||
resolvePlugins(); | |||
try { | |||
resolvePlugins(); | |||
} catch (PluginException e) { | |||
LOG.error(e.getMessage(), e); | |||
} | |||
} | |||
/** | |||
@@ -211,11 +210,17 @@ public class DefaultPluginManager implements PluginManager { | |||
return pluginClassLoaders.get(pluginId); | |||
} | |||
public <T> List<ExtensionWrapper<T>> getExtensions(Class<T> type) { | |||
return extensionFinder.find(type); | |||
public <T> List<T> getExtensions(Class<T> type) { | |||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type); | |||
List<T> extensions = new ArrayList<T>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
extensions.add(extensionWrapper.getInstance()); | |||
} | |||
return extensions; | |||
} | |||
private void loadPlugin(String fileName) throws Exception { | |||
private void loadPlugin(String fileName) throws PluginException { | |||
// test for plugin directory | |||
File pluginDirectory = new File(pluginsDirectory, fileName); | |||
if (!pluginDirectory.isDirectory()) { | |||
@@ -244,25 +249,20 @@ public class DefaultPluginManager implements PluginManager { | |||
// load plugin | |||
LOG.debug("Loading plugin '" + pluginPath + "'"); | |||
PluginWrapper pluginWrapper = new PluginWrapper(pluginDescriptor); | |||
PluginLoader pluginLoader = new PluginLoader(this, pluginWrapper, pluginDirectory); | |||
PluginLoader pluginLoader = new PluginLoader(this, pluginDescriptor, pluginDirectory); | |||
pluginLoader.load(); | |||
LOG.debug("Loaded plugin '" + pluginPath + "'"); | |||
// set some variables in plugin wrapper | |||
pluginWrapper.setPluginPath(pluginPath); | |||
pluginWrapper.setPluginClassLoader(pluginLoader.getPluginClassLoader()); | |||
// create the plugin instance | |||
LOG.debug("Creating instance for plugin '" + pluginPath + "'"); | |||
Plugin plugin = getPluginInstance(pluginWrapper, pluginLoader); | |||
LOG.debug("Created instance '" + plugin + "' for 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 '" + pluginWrapper + "' for plugin '" + pluginPath + "'"); | |||
String pluginId = pluginDescriptor.getPluginId(); | |||
// add plugin to the list with plugins | |||
plugins.put(pluginId, plugin); | |||
unresolvedPlugins.add(plugin); | |||
plugins.put(pluginId, pluginWrapper); | |||
unresolvedPlugins.add(pluginWrapper); | |||
// add plugin class loader to the list with class loaders | |||
PluginClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); | |||
@@ -289,39 +289,17 @@ public class DefaultPluginManager implements PluginManager { | |||
} | |||
} | |||
private Plugin getPluginInstance(PluginWrapper pluginWrapper, PluginLoader pluginLoader) | |||
throws Exception { | |||
String pluginClassName = pluginWrapper.getDescriptor().getPluginClass(); | |||
ClassLoader pluginClassLoader = pluginLoader.getPluginClassLoader(); | |||
Class<?> pluginClass = pluginClassLoader.loadClass(pluginClassName); | |||
// once we have the class, we can do some checks on it to ensure | |||
// that it is a valid implementation of a plugin. | |||
int modifiers = pluginClass.getModifiers(); | |||
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) | |||
|| (!Plugin.class.isAssignableFrom(pluginClass))) { | |||
throw new PluginException("The plugin class '" + pluginClassName | |||
+ "' is not compatible."); | |||
} | |||
// create the plugin instance | |||
Constructor<?> constructor = pluginClass.getConstructor(new Class[] { PluginWrapper.class }); | |||
Plugin plugin = (Plugin) constructor.newInstance(new Object[] { pluginWrapper }); | |||
return plugin; | |||
} | |||
private void resolvePlugins() { | |||
resolveDependencies(); | |||
private void resolvePlugins() throws PluginException { | |||
resolveDependencies(); | |||
} | |||
private void resolveDependencies() { | |||
private void resolveDependencies() throws PluginException { | |||
DependencyResolver dependencyResolver = new DependencyResolver(unresolvedPlugins); | |||
resolvedPlugins = dependencyResolver.getSortedDependencies(); | |||
for (Plugin plugin : resolvedPlugins) { | |||
unresolvedPlugins.remove(plugin); | |||
uberClassLoader.addLoader(plugin.getWrapper().getPluginClassLoader()); | |||
resolvedPlugins = dependencyResolver.getSortedPlugins(); | |||
for (PluginWrapper pluginWrapper : resolvedPlugins) { | |||
unresolvedPlugins.remove(pluginWrapper); | |||
uberClassLoader.addLoader(pluginWrapper.getPluginClassLoader()); | |||
LOG.info("Plugin '" + pluginWrapper.getDescriptor().getPluginId() + "' resolved"); | |||
} | |||
} | |||
@@ -28,24 +28,24 @@ class DependencyResolver { | |||
private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class); | |||
private List<Plugin> plugins; | |||
private List<PluginWrapper> plugins; | |||
public DependencyResolver(List<Plugin> plugins) { | |||
public DependencyResolver(List<PluginWrapper> plugins) { | |||
this.plugins = plugins; | |||
} | |||
/** | |||
* Get the list of plugins in dependency sorted order. | |||
*/ | |||
public List<Plugin> getSortedDependencies() { | |||
public List<PluginWrapper> getSortedPlugins() throws PluginException { | |||
DirectedGraph<String> graph = new DirectedGraph<String>(); | |||
for (Plugin plugin : plugins) { | |||
PluginDescriptor descriptor = plugin.getWrapper().getDescriptor(); | |||
for (PluginWrapper pluginWrapper : plugins) { | |||
PluginDescriptor descriptor = pluginWrapper.getDescriptor(); | |||
String pluginId = descriptor.getPluginId(); | |||
List<String> dependencies = descriptor.getDependencies(); | |||
List<PluginDependency> dependencies = descriptor.getDependencies(); | |||
if (!dependencies.isEmpty()) { | |||
for (String dependency : dependencies) { | |||
graph.addEdge(pluginId, dependency); | |||
for (PluginDependency dependency : dependencies) { | |||
graph.addEdge(pluginId, dependency.getPluginId()); | |||
} | |||
} else { | |||
graph.addVertex(pluginId); | |||
@@ -56,12 +56,11 @@ class DependencyResolver { | |||
List<String> pluginsId = graph.reverseTopologicalSort(); | |||
if (pluginsId == null) { | |||
LOG.error("Cyclic dependences !!!"); | |||
return null; | |||
throw new CyclicDependencyException("Cyclic dependences !!!" + graph.toString()); | |||
} | |||
LOG.debug("Plugins order: " + pluginsId); | |||
List<Plugin> sortedPlugins = new ArrayList<Plugin>(); | |||
List<PluginWrapper> sortedPlugins = new ArrayList<PluginWrapper>(); | |||
for (String pluginId : pluginsId) { | |||
sortedPlugins.add(getPlugin(pluginId)); | |||
} | |||
@@ -69,14 +68,14 @@ class DependencyResolver { | |||
return sortedPlugins; | |||
} | |||
private Plugin getPlugin(String pluginId) { | |||
for (Plugin plugin : plugins) { | |||
if (pluginId.equals(plugin.getWrapper().getDescriptor().getPluginId())) { | |||
return plugin; | |||
private PluginWrapper getPlugin(String pluginId) throws PluginNotFoundException { | |||
for (PluginWrapper pluginWrapper : plugins) { | |||
if (pluginId.equals(pluginWrapper.getDescriptor().getPluginId())) { | |||
return pluginWrapper; | |||
} | |||
} | |||
return null; | |||
throw new PluginNotFoundException(pluginId); | |||
} | |||
} |
@@ -28,13 +28,13 @@ class PluginClassLoader extends URLClassLoader { | |||
private static final String PLUGIN_PACKAGE_PREFIX = "ro.fortsoft.pf4j."; | |||
private PluginManager pluginManager; | |||
private PluginWrapper pluginWrapper; | |||
public PluginClassLoader(PluginManager pluginManager, PluginWrapper pluginWrapper, ClassLoader parent) { | |||
private PluginDescriptor pluginDescriptor; | |||
public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) { | |||
super(new URL[0], parent); | |||
this.pluginManager = pluginManager; | |||
this.pluginWrapper = pluginWrapper; | |||
this.pluginDescriptor = pluginDescriptor; | |||
} | |||
@Override | |||
@@ -74,9 +74,9 @@ class PluginClassLoader extends URLClassLoader { | |||
} | |||
// look in dependencies | |||
List<String> dependencies = pluginWrapper.getDescriptor().getDependencies(); | |||
for (String dependency : dependencies) { | |||
PluginClassLoader classLoader = pluginManager.getPluginClassLoader(dependency); | |||
List<PluginDependency> dependencies = pluginDescriptor.getDependencies(); | |||
for (PluginDependency dependency : dependencies) { | |||
PluginClassLoader classLoader = pluginManager.getPluginClassLoader(dependency.getPluginId()); | |||
try { | |||
return classLoader.loadClass(className); | |||
} catch (ClassNotFoundException e) { |
@@ -0,0 +1,49 @@ | |||
/* | |||
* 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; | |||
/** | |||
* @author Decebal Suiu | |||
*/ | |||
public class PluginDependency { | |||
private String pluginId; | |||
private PluginVersion pluginVersion; | |||
public PluginDependency(String dependency) { | |||
/* | |||
int index = dependency.indexOf(':'); | |||
if (index == -1) { | |||
throw new IllegalArgumentException("Illegal dependency specifier "+ dependency); | |||
} | |||
this.pluginId = dependency.substring(0, index); | |||
this.pluginVersion = PluginVersion.createVersion(dependency.substring(index + 1)); | |||
*/ | |||
this.pluginId = dependency; | |||
} | |||
public String getPluginId() { | |||
return pluginId; | |||
} | |||
public PluginVersion getPluginVersion() { | |||
return pluginVersion; | |||
} | |||
@Override | |||
public String toString() { | |||
return "PluginDependency [pluginId=" + pluginId + ", pluginVersion=" + pluginVersion + "]"; | |||
} | |||
} |
@@ -33,12 +33,11 @@ class PluginDescriptor { | |||
private String pluginClass; | |||
private PluginVersion version; | |||
private String provider; | |||
private String pluginPath; | |||
private List<String> dependencies; | |||
private List<PluginDependency> dependencies; | |||
private PluginClassLoader pluginClassLoader; | |||
public PluginDescriptor() { | |||
dependencies = new ArrayList<String>(); | |||
dependencies = new ArrayList<PluginDependency>(); | |||
} | |||
/** | |||
@@ -69,18 +68,11 @@ class PluginDescriptor { | |||
return provider; | |||
} | |||
/** | |||
* Returns the path of this plugin relative to plugins directory. | |||
*/ | |||
public String getPluginPath() { | |||
return pluginPath; | |||
} | |||
/** | |||
* Returns all dependencies declared by this plugin. | |||
* Returns an empty array if this plugin does not declare any require. | |||
*/ | |||
public List<String> getDependencies() { | |||
public List<PluginDependency> getDependencies() { | |||
return dependencies; | |||
} | |||
@@ -100,7 +92,6 @@ class PluginDescriptor { | |||
.append("pluginClass", pluginClass) | |||
.append("version", version) | |||
.append("provider", provider) | |||
.append("pluginPath", pluginPath) | |||
.append("dependencies", dependencies) | |||
.toString(); | |||
} | |||
@@ -120,14 +111,14 @@ class PluginDescriptor { | |||
void setProvider(String provider) { | |||
this.provider = provider; | |||
} | |||
void setPluginPath(String pluginPath) { | |||
this.pluginPath = pluginPath; | |||
} | |||
void setDependencies(String dependencies) { | |||
if (dependencies != null) { | |||
this.dependencies = Arrays.asList(StringUtils.split(dependencies, ',')); | |||
this.dependencies = new ArrayList<PluginDependency>(); | |||
List<String> tokens = Arrays.asList(StringUtils.split(dependencies, ',')); | |||
for (String dependency : tokens) { | |||
this.dependencies.add(new PluginDependency(dependency)); | |||
} | |||
} else { | |||
this.dependencies = Collections.emptyList(); | |||
} |
@@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory; | |||
import ro.fortsoft.pf4j.util.DirectoryFilter; | |||
import ro.fortsoft.pf4j.util.JarFilter; | |||
/** | |||
* Load all informations needed by a plugin. | |||
* This means add all jar files from 'lib' directory, 'classes' | |||
@@ -53,12 +52,12 @@ class PluginLoader { | |||
private PluginClassLoader pluginClassLoader; | |||
public PluginLoader(PluginManager pluginManager, PluginWrapper pluginWrapper, File pluginRepository) { | |||
public PluginLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, File pluginRepository) { | |||
this.pluginRepository = pluginRepository; | |||
classesDirectory = new File(pluginRepository, "classes"); | |||
libDirectory = new File(pluginRepository, "lib"); | |||
ClassLoader parent = getClass().getClassLoader(); | |||
pluginClassLoader = new PluginClassLoader(pluginManager, pluginWrapper, parent); | |||
pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, parent); | |||
LOG.debug("Created class loader " + pluginClassLoader); | |||
} | |||
@@ -25,7 +25,7 @@ public interface PluginManager { | |||
/** | |||
* Retrieves all plugins. | |||
*/ | |||
public List<Plugin> getPlugins(); | |||
public List<PluginWrapper> getPlugins(); | |||
/** | |||
* Load plugins. | |||
@@ -44,6 +44,6 @@ public interface PluginManager { | |||
public PluginClassLoader getPluginClassLoader(String pluginId); | |||
public <T> List<ExtensionWrapper<T>> getExtensions(Class<T> type); | |||
public <T> List<T> getExtensions(Class<T> type); | |||
} |
@@ -0,0 +1,34 @@ | |||
/* | |||
* 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; | |||
/** | |||
* @author Decebal Suiu | |||
*/ | |||
class PluginNotFoundException extends PluginException { | |||
private static final long serialVersionUID = 1L; | |||
private String pluginId; | |||
public PluginNotFoundException(String pluginId) { | |||
super("Plugin '" + pluginId + "' not found."); | |||
this.pluginId = pluginId; | |||
} | |||
public String getPluginId() { | |||
return pluginId; | |||
} | |||
} |
@@ -12,6 +12,12 @@ | |||
*/ | |||
package ro.fortsoft.pf4j; | |||
import java.lang.reflect.Constructor; | |||
import java.lang.reflect.Modifier; | |||
import org.apache.commons.lang.builder.ToStringBuilder; | |||
import org.apache.commons.lang.builder.ToStringStyle; | |||
/** | |||
* A wrapper over plugin instance. | |||
* | |||
@@ -22,9 +28,20 @@ public class PluginWrapper { | |||
PluginDescriptor descriptor; | |||
String pluginPath; | |||
PluginClassLoader pluginClassLoader; | |||
Plugin plugin; | |||
public PluginWrapper(PluginDescriptor descriptor) { | |||
public PluginWrapper(PluginDescriptor descriptor, String pluginPath, PluginClassLoader pluginClassLoader) { | |||
this.descriptor = descriptor; | |||
this.pluginPath = pluginPath; | |||
this.pluginClassLoader = pluginClassLoader; | |||
// TODO | |||
try { | |||
plugin = createPluginInstance(); | |||
} catch (Exception e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} | |||
} | |||
/** | |||
@@ -50,12 +67,36 @@ public class PluginWrapper { | |||
return pluginClassLoader; | |||
} | |||
void setPluginPath(String pluginPath) { | |||
this.pluginPath = pluginPath; | |||
public Plugin getPlugin() { | |||
return plugin; | |||
} | |||
void setPluginClassLoader(PluginClassLoader pluginClassLoader) { | |||
this.pluginClassLoader = pluginClassLoader; | |||
} | |||
private Plugin createPluginInstance() throws Exception { | |||
String pluginClassName = descriptor.getPluginClass(); | |||
Class<?> pluginClass = pluginClassLoader.loadClass(pluginClassName); | |||
// once we have the class, we can do some checks on it to ensure | |||
// that it is a valid implementation of a plugin. | |||
int modifiers = pluginClass.getModifiers(); | |||
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) | |||
|| (!Plugin.class.isAssignableFrom(pluginClass))) { | |||
throw new PluginException("The plugin class '" + pluginClassName + "' is not compatible."); | |||
} | |||
// create the plugin instance | |||
Constructor<?> constructor = pluginClass.getConstructor(new Class[] { PluginWrapper.class }); | |||
Plugin plugin = (Plugin) constructor.newInstance(new Object[] { this }); | |||
return plugin; | |||
} | |||
@Override | |||
public String toString() { | |||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) | |||
.append("descriptor", descriptor) | |||
.append("pluginPath", pluginPath) | |||
.append("plugin", plugin) | |||
.toString(); | |||
} | |||
} |