--- /dev/null
+/*
+ * Copyright (C) 2012-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License 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 org.pf4j;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * {@link ClassLoadingStrategy} will be used to configure {@link PluginClassLoader} loading order
+ * and contains all possible options supported by {@link PluginClassLoader} where
+ * A=Application Source ( load classes from parent classLoader)
+ * P=Plugin Source ( load classes from this classloader)
+ * D=Dependencies ( load classes from dependencies)
+ */
+public class ClassLoadingStrategy {
+
+ /**
+ * application(parent)->plugin->dependencies
+ */
+ public static final ClassLoadingStrategy APD = new ClassLoadingStrategy(Arrays.asList(Source.APPLICATION, Source.PLUGIN, Source.DEPENDENCIES));
+ /**
+ * application(parent)->dependencies->plugin
+ */
+ public static final ClassLoadingStrategy ADP=new ClassLoadingStrategy(Arrays.asList(Source.APPLICATION,Source.DEPENDENCIES,Source.APPLICATION));
+ /**
+ * plugin->application(parent)->dependencies
+ */
+ public static final ClassLoadingStrategy PAD=new ClassLoadingStrategy(Arrays.asList(Source.PLUGIN,Source.APPLICATION,Source.DEPENDENCIES));
+ /**
+ * dependencies->application(parent)->plugin
+ */
+ public static final ClassLoadingStrategy DAP=new ClassLoadingStrategy(Arrays.asList(Source.DEPENDENCIES,Source.APPLICATION,Source.PLUGIN));
+ /**
+ * dependencies->plugin->application(parent)
+ */
+ public static final ClassLoadingStrategy DPA=new ClassLoadingStrategy(Arrays.asList(Source.DEPENDENCIES,Source.PLUGIN,Source.APPLICATION));
+ /**
+ * plugin->dependencies->application(parent)
+ */
+ public static final ClassLoadingStrategy PDA =new ClassLoadingStrategy(Arrays.asList(Source.PLUGIN, Source.DEPENDENCIES, Source.APPLICATION));
+
+
+ private final List<Source> sources;
+
+ private ClassLoadingStrategy(List<Source> sources) {
+ this.sources = sources;
+ }
+
+ public List<Source> getSources() {
+ return sources;
+ }
+
+ public enum Source {
+ PLUGIN, APPLICATION,DEPENDENCIES;
+ }
+}
* One instance of this class should be created by plugin manager for every available plug-in.
* By default, this class loader is a Parent Last ClassLoader - it loads the classes from the plugin's jars
* before delegating to the parent class loader.
- * Use {@link #parentFirst} to change the loading strategy.
+ * Use {@link #classLoadingStrategy} to change the loading strategy.
*
* @author Decebal Suiu
*/
private PluginManager pluginManager;
private PluginDescriptor pluginDescriptor;
- private boolean parentFirst;
+ private ClassLoadingStrategy classLoadingStrategy;
public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) {
this(pluginManager, pluginDescriptor, parent, false);
* before trying to load the a class through this loader.
*/
public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent, boolean parentFirst) {
+ this(pluginManager, pluginDescriptor, parent, parentFirst ? ClassLoadingStrategy.APD : ClassLoadingStrategy.PDA);
+ }
+
+
+ /**
+ * classloading according to {@code classLoadingStrategy}
+ */
+ public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent, ClassLoadingStrategy classLoadingStrategy) {
super(new URL[0], parent);
this.pluginManager = pluginManager;
this.pluginDescriptor = pluginDescriptor;
- this.parentFirst = parentFirst;
+ this.classLoadingStrategy = classLoadingStrategy;
}
@Override
* By default, it 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 {@link ClassLoader#loadClass(String)} mechanism.
- * Use {@link #parentFirst} to change the loading strategy.
+ * Use {@link #classLoadingStrategy} to change the loading strategy.
*/
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
log.trace("Found loaded class '{}'", className);
return loadedClass;
}
-
- if (!parentFirst) {
- // nope, try to load locally
- try {
- loadedClass = findClass(className);
- log.trace("Found class '{}' in plugin classpath", className);
- return loadedClass;
- } catch (ClassNotFoundException e) {
- // try next step
- }
-
- // look in dependencies
- loadedClass = loadClassFromDependencies(className);
- if (loadedClass != null) {
- log.trace("Found class '{}' in dependencies", className);
- return loadedClass;
- }
-
- log.trace("Couldn't find class '{}' in plugin classpath. Delegating to parent", className);
-
- // use the standard ClassLoader (which follows normal parent delegation)
- return super.loadClass(className);
- } else {
- // try to load from parent
+ for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
+ Class<?> c = null;
try {
- return super.loadClass(className);
- } catch (ClassNotFoundException e) {
- // try next step
- }
-
- log.trace("Couldn't find class '{}' in parent. Delegating to plugin classpath", className);
+ switch (classLoadingSource) {
+ case APPLICATION:
+ c = super.loadClass(className);
+ break;
+ case PLUGIN:
+ c = findClass(className);
+ break;
+ case DEPENDENCIES:
+ c = loadClassFromDependencies(className);
+ break;
+ }
+ } catch (ClassNotFoundException ignored) {
- // nope, try to load locally
- try {
- loadedClass = findClass(className);
- log.trace("Found class '{}' in plugin classpath", className);
- return loadedClass;
- } catch (ClassNotFoundException e) {
- // try next step
}
-
- // look in dependencies
- loadedClass = loadClassFromDependencies(className);
- if (loadedClass != null) {
- log.trace("Found class '{}' in dependencies", className);
- return loadedClass;
+ if (c != null) {
+ log.trace("Found class '{}' in {} classpath", className,classLoadingSource);
+ return c;
+ } else {
+ log.trace("Couldn't find class '{}' in {} classpath", className,classLoadingSource);
}
- throw new ClassNotFoundException(className);
}
+ throw new ClassNotFoundException(className);
}
}
/**
* Load the named resource from this plugin.
* By default, this implementation checks the plugin's classpath first then delegates to the parent.
- * Use {@link #parentFirst} to change the loading strategy.
+ * Use {@link #classLoadingStrategy} to change the loading strategy.
*
* @param name the name of the resource.
* @return the URL to the resource, {@code null} if the resource was not found.
@Override
public URL getResource(String name) {
log.trace("Received request to load resource '{}'", name);
- if (!parentFirst) {
- URL url = findResource(name);
- if (url != null) {
- log.trace("Found resource '{}' in plugin classpath", name);
- return url;
+ for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
+ URL url = null;
+ switch (classLoadingSource) {
+ case APPLICATION:
+ url = super.getResource(name);
+ break;
+ case PLUGIN:
+ url = findResource(name);
+ break;
+ case DEPENDENCIES:
+ url = findResourceFromDependencies(name);
+ break;
}
-
- url = findResourceFromDependencies(name);
if (url != null) {
- log.trace("Found resource '{}' in plugin dependencies", name);
+ log.trace("Found resource '{}' in {} classpath", name,classLoadingSource);
return url;
+ } else {
+ log.trace("Couldn't find resource '{}' in {}.", name,classLoadingSource);
}
-
- log.trace("Couldn't find resource '{}' in plugin or dependencies classpath. Delegating to parent", name);
-
- return super.getResource(name);
- } else {
- URL url = super.getResource(name);
- if (url != null) {
- log.trace("Found resource '{}' in parent", name);
- return url;
- }
-
- log.trace("Couldn't find resource '{}' in parent", name);
-
- url = findResourceFromDependencies(name);
-
- if (url != null) {
- log.trace("Found resource '{}' in dependencies", name);
- return url;
- }
-
- return findResource(name);
}
+ return null;
+
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
- List<URL> resources = new ArrayList<>();
-
- if (!parentFirst) {
- resources.addAll(Collections.list(findResources(name)));
- resources.addAll(findResourcesFromDependencies(name));
-
- if (getParent() != null) {
- resources.addAll(Collections.list(getParent().getResources(name)));
- }
- } else {
- if (getParent() != null) {
- resources.addAll(Collections.list(getParent().getResources(name)));
+ List<URL> resources = new ArrayList<>();
+
+ log.trace("Received request to load resources '{}'", name);
+ for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
+ switch (classLoadingSource) {
+ case APPLICATION:
+ if (getParent() != null) {
+ resources.addAll(Collections.list(getParent().getResources(name)));
+ }
+ break;
+ case PLUGIN:
+ resources.addAll(Collections.list(findResources(name)));
+ break;
+ case DEPENDENCIES:
+ resources.addAll(findResourcesFromDependencies(name));
+ break;
}
-
- resources.addAll(findResourcesFromDependencies(name));
- resources.addAll(Collections.list(super.findResources(name)));
}
+ return Collections.enumeration(resources);
- return Collections.enumeration(resources);
}
- private Class<?> loadClassFromDependencies(String className) {
+ protected Class<?> loadClassFromDependencies(String className) {
log.trace("Search in dependencies for class '{}'", className);
List<PluginDependency> dependencies = pluginDescriptor.getDependencies();
for (PluginDependency dependency : dependencies) {
return null;
}
- private URL findResourceFromDependencies(String name) {
+ protected URL findResourceFromDependencies(String name) {
log.trace("Search in dependencies for resource '{}'", name);
List<PluginDependency> dependencies = pluginDescriptor.getDependencies();
for (PluginDependency dependency : dependencies) {
URL url = classLoader.findResource(name);
if (Objects.nonNull(url)) {
- return url;
+ return url;
}
}
return null;
}
- private Collection<URL> findResourcesFromDependencies(String name) throws IOException {
+ protected Collection<URL> findResourcesFromDependencies(String name) throws IOException {
log.trace("Search in dependencies for resources '{}'", name);
List<URL> results = new ArrayList<>();
List<PluginDependency> dependencies = pluginDescriptor.getDependencies();