diff options
author | asafbennatan <asafbennatan@gmail.com> | 2020-07-17 16:38:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-17 16:38:38 +0300 |
commit | 53745be62c939ee7a6891a2d9507d4da790fa47c (patch) | |
tree | bc92ccd5c16ca8f2a4028844a20d11af2240ca8a | |
parent | b72b75ae26e4bbab7ce4a597484da4494825b655 (diff) | |
download | pf4j-53745be62c939ee7a6891a2d9507d4da790fa47c.tar.gz pf4j-53745be62c939ee7a6891a2d9507d4da790fa47c.zip |
Better Customization for PluginClassLoader (#385)
-rw-r--r-- | pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java | 69 | ||||
-rw-r--r-- | pf4j/src/main/java/org/pf4j/PluginClassLoader.java | 169 |
2 files changed, 140 insertions, 98 deletions
diff --git a/pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java b/pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java new file mode 100644 index 0000000..5a261cc --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java @@ -0,0 +1,69 @@ +/* + * 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; + } +} diff --git a/pf4j/src/main/java/org/pf4j/PluginClassLoader.java b/pf4j/src/main/java/org/pf4j/PluginClassLoader.java index 6d609c1..fc771de 100644 --- a/pf4j/src/main/java/org/pf4j/PluginClassLoader.java +++ b/pf4j/src/main/java/org/pf4j/PluginClassLoader.java @@ -33,7 +33,7 @@ import java.util.Objects; * 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 */ @@ -46,7 +46,7 @@ public class PluginClassLoader extends URLClassLoader { 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); @@ -57,11 +57,19 @@ public class PluginClassLoader extends URLClassLoader { * 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 @@ -83,7 +91,7 @@ public class PluginClassLoader extends URLClassLoader { * 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 { @@ -107,63 +115,39 @@ public class PluginClassLoader extends URLClassLoader { 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. @@ -171,66 +155,55 @@ public class PluginClassLoader extends URLClassLoader { @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) { @@ -251,7 +224,7 @@ public class PluginClassLoader extends URLClassLoader { 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) { @@ -264,14 +237,14 @@ public class PluginClassLoader extends URLClassLoader { 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(); |