Browse Source

Better Customization for PluginClassLoader (#385)

pull/388/head
asafbennatan 5 years ago committed by GitHub
parent
commit
53745be62c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 69
      pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java
  2. 153
      pf4j/src/main/java/org/pf4j/PluginClassLoader.java

69
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;
}
}

153
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
for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
Class<?> c = null;
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);
switch (classLoadingSource) {
case APPLICATION:
c = super.loadClass(className);
break;
case PLUGIN:
c = findClass(className);
break;
case DEPENDENCIES:
c = loadClassFromDependencies(className);
break;
}
} catch (ClassNotFoundException ignored) {
}
if (c != null) {
log.trace("Found class '{}' in {} classpath", className,classLoadingSource);
return c;
} else {
// try to load from parent
try {
return super.loadClass(className);
} catch (ClassNotFoundException e) {
// try next step
}
log.trace("Couldn't find class '{}' in parent. Delegating to plugin classpath", className);
// 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
log.trace("Couldn't find class '{}' in {} classpath", className,classLoadingSource);
}
// look in dependencies
loadedClass = loadClassFromDependencies(className);
if (loadedClass != null) {
log.trace("Found class '{}' in dependencies", className);
return loadedClass;
}
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);
if (url != null) {
log.trace("Found resource '{}' in plugin dependencies", name);
return url;
break;
}
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);
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 parent", name);
url = findResourceFromDependencies(name);
if (url != null) {
log.trace("Found resource '{}' in dependencies", name);
return url;
}
return null;
return findResource(name);
}
}
@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 {
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));
resources.addAll(Collections.list(super.findResources(name)));
break;
}
}
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) {
@ -271,7 +244,7 @@ public class PluginClassLoader extends URLClassLoader {
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();

Loading…
Cancel
Save