>>> Welcome
>>> Hello
+**NOTE:** Starting with version 0.9 you can define an extension directly in a jar (in classpath). See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/ro/fortsoft/pf4j/WhazzupGreeting.java) for a real example.
+
You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, ...) in DefaultPluginManager just override `create...` methods (factory method pattern).
Example:
System.out.println(">>> " + greeting.getGreeting());\r
}\r
\r
+ // print extensions from classpath (non plugin)\r
+ System.out.println(String.format("Extensions added by classpath:"));\r
+ Set<String> extensionClassNames = pluginManager.getExtensionClassNames(null);\r
+ for (String extension : extensionClassNames) {\r
+ System.out.println(" " + extension);\r
+ }\r
+\r
// print extensions for each started plugin\r
List<PluginWrapper> startedPlugins = pluginManager.getStartedPlugins();\r
for (PluginWrapper plugin : startedPlugins) {\r
String pluginId = plugin.getDescriptor().getPluginId();\r
System.out.println(String.format("Extensions added by plugin '%s':", pluginId));\r
- Set<String> extensionClassNames = pluginManager.getExtensionClassNames(pluginId);\r
+ extensionClassNames = pluginManager.getExtensionClassNames(pluginId);\r
for (String extension : extensionClassNames) {\r
System.out.println(" " + extension);\r
}\r
--- /dev/null
+/*
+ * 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.demo;
+
+import ro.fortsoft.pf4j.Extension;
+import ro.fortsoft.pf4j.demo.api.Greeting;
+
+/**
+ * @author Decebal Suiu
+ */
+@Extension
+public class WhazzupGreeting implements Greeting {
+
+ @Override
+ public String getGreeting() {
+ return "Whazzup";
+ }
+
+}
# PF4J log
#
log4j.logger.ro.fortsoft.pf4j=DEBUG, Console
+log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=WARN, Console
log4j.additivity.ro.fortsoft.pf4j=false
#
for (Map.Entry<String, Set<String>> entry : entries.entrySet()) {
String pluginId = entry.getKey();
- PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
- if (PluginState.STARTED != pluginWrapper.getPluginState()) {
- continue;
+ if (pluginId != null) {
+ PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
+ if (PluginState.STARTED != pluginWrapper.getPluginState()) {
+ continue;
+ }
}
Set<String> extensionClassNames = entry.getValue();
for (String className : extensionClassNames) {
try {
- Class<?> extensionType = pluginManager.getPluginClassLoader(pluginId).loadClass(className);
+ Class<?> extensionType;
+ if (pluginId != null) {
+ extensionType = pluginManager.getPluginClassLoader(pluginId).loadClass(className);
+ } else {
+ extensionType = getClass().getClassLoader().loadClass(className);
+ }
+
log.debug("Checking extension type '{}'", extensionType.getName());
if (type.isAssignableFrom(extensionType)) {
Object instance = extensionFactory.create(extensionType);
return entries;
}
- entries = new HashMap<String, Set<String>>();
+ entries = new LinkedHashMap<String, Set<String>>();
+
+ readClasspathIndexFiles();
+ readPluginsIndexFiles();
+
+ return entries;
+ }
+
+ private void readClasspathIndexFiles() {
+ log.debug("Reading extensions index files from classpath");
+
+ Set<String> bucket = new HashSet<String>();
+ try {
+ Enumeration<URL> urls = getClass().getClassLoader().getResources(ExtensionsIndexer.EXTENSIONS_RESOURCE);
+ while (urls.hasMoreElements()) {
+ URL url = urls.nextElement();
+ log.debug("Read '{}'", url.getFile());
+ Reader reader = new InputStreamReader(url.openStream(), "UTF-8");
+ ExtensionsIndexer.readIndex(reader, bucket);
+ }
+
+ if (bucket.isEmpty()) {
+ log.debug("No extensions found");
+ } else {
+ log.debug("Found possible {} extensions:", bucket.size());
+ for (String entry : bucket) {
+ log.debug(" " + entry);
+ }
+ }
+
+ entries.put(null, bucket);
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ private void readPluginsIndexFiles() {
+ log.debug("Reading extensions index files from plugins");
List<PluginWrapper> plugins = pluginManager.getPlugins();
for (PluginWrapper plugin : plugins) {
String pluginId = plugin.getDescriptor().getPluginId();
log.debug("Reading extensions index file for plugin '{}'", pluginId);
- Set<String> entriesPerPlugin = new HashSet<String>();
+ Set<String> bucket = new HashSet<String>();
try {
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);
+ ExtensionsIndexer.readIndex(reader, bucket);
- if (entriesPerPlugin.isEmpty()) {
+ if (bucket.isEmpty()) {
log.debug("No extensions found");
} else {
- log.debug("Found possible {} extensions:", entriesPerPlugin.size());
- for (String entry : entriesPerPlugin) {
+ log.debug("Found possible {} extensions:", bucket.size());
+ for (String entry : bucket) {
log.debug(" " + entry);
}
}
- entries.put(pluginId, entriesPerPlugin);
+ entries.put(pluginId, bucket);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
-
- return entries;
}
private boolean isExtensionPoint(Class<?> type) {
private static final Logger log = LoggerFactory.getLogger(PluginClassLoader.class);
-// private static final String JAVA_PACKAGE_PREFIX = "java.";
-// private static final String JAVAX_PACKAGE_PREFIX = "javax.";
private static final String PLUGIN_PACKAGE_PREFIX = "ro.fortsoft.pf4j.";
private PluginManager pluginManager;
super.addURL(url);
}
+ /**
+ * This implementation of loadClass uses a child first delegation model rather than the standard parent first.
+ * If the requested class cannot be found in this class loader, the parent class loader will be consulted
+ * via the standard ClassLoader.loadClass(String) mechanism.
+ */
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
-// System.out.println(">>>" + className);
-
- /*
- // javax.mail is not in JDK ?!
- // first check whether it's a system class, delegate to the system loader
- if (className.startsWith(JAVA_PACKAGE_PREFIX) || className.startsWith(JAVAX_PACKAGE_PREFIX)) {
- return findSystemClass(className);
- }
- */
-
+ log.debug("Received request to load class '{}'", className);
// if the class it's a part of the plugin engine use parent class loader
if (className.startsWith(PLUGIN_PACKAGE_PREFIX)) {
+ log.debug("Delegate the loading of class '{}' to parent", className);
try {
- return PluginClassLoader.class.getClassLoader().loadClass(className);
+ return getClass().getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
// try next step
+ // TODO if I uncomment below lines (the correct approach) I received ClassNotFoundException for demo (ro.fortsoft.pf4j.demo)
+// log.error(e.getMessage(), e);
+// throw e;
}
}
// second check whether it's already been loaded
- Class<?> loadedClass = findLoadedClass(className);
- if (loadedClass != null) {
- return loadedClass;
+ Class<?> clazz = findLoadedClass(className);
+ if (clazz != null) {
+ log.debug("Found loaded class '{}'", className);
+ return clazz;
}
// nope, try to load locally
try {
- return findClass(className);
+ clazz = findClass(className);
+ log.debug("Found class '{}' in plugin classpath", className);
+ return clazz;
} catch (ClassNotFoundException e) {
// try next step
}
// look in dependencies
+ log.debug("Look in dependencies for class '{}'", className);
List<PluginDependency> dependencies = pluginDescriptor.getDependencies();
for (PluginDependency dependency : dependencies) {
PluginClassLoader classLoader = pluginManager.getPluginClassLoader(dependency.getPluginId());
}
}
+ log.debug("Couldn't find class '{}' in plugin classpath. Delegating to parent");
+
// use the standard URLClassLoader (which follows normal parent delegation)
return super.loadClass(className);
}
+ /**
+ * Load the named resource from this plugin. This implementation checks the plugin's classpath first
+ * then delegates to the parent.
+ *
+ * @param name the name of the resource.
+ * @return the URL to the resource, <code>null</code> if the resource was not found.
+ */
+ @Override
+ public URL getResource(String name) {
+ log.debug("Trying to find resource '{}' in plugin classpath", name);
+ URL url = findResource(name);
+ if (url != null) {
+ log.debug("Found resource '{}' in plugin classpath", name);
+ return url;
+ }
+
+ log.debug("Couldn't find resource '{}' in plugin classpath. Delegating to parent");
+
+ return super.getResource(name);
+ }
+
/**
* Release all resources acquired by this class loader.
* The current implementation is incomplete.