]> source.dussan.org Git - pf4j.git/commitdiff
Better Customization for PluginClassLoader (#385)
authorasafbennatan <asafbennatan@gmail.com>
Fri, 17 Jul 2020 13:38:38 +0000 (16:38 +0300)
committerGitHub <noreply@github.com>
Fri, 17 Jul 2020 13:38:38 +0000 (16:38 +0300)
pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java [new file with mode: 0644]
pf4j/src/main/java/org/pf4j/PluginClassLoader.java

diff --git a/pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java b/pf4j/src/main/java/org/pf4j/ClassLoadingStrategy.java
new file mode 100644 (file)
index 0000000..5a261cc
--- /dev/null
@@ -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;
+    }
+}
index 6d609c1cff183692319f254d7ee2af01a0bf236d..fc771de1fa418638488ec18429abd39dea5ebd56 100644 (file)
@@ -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();