]> source.dussan.org Git - pf4j.git/commitdiff
Bucketed caching in SingletonExtensionFactory (#402)
authorAjith Kumar <ajithalbus@gmail.com>
Tue, 20 Oct 2020 09:06:15 +0000 (14:36 +0530)
committerGitHub <noreply@github.com>
Tue, 20 Oct 2020 09:06:15 +0000 (12:06 +0300)
pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java
pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java

index 725b9a7b32bd7da7b72c6fc14e8424496ca22acf..ff94652dcfcf8b516d32d0349085a13309f14c69 100644 (file)
@@ -19,36 +19,46 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * An {@link ExtensionFactory} that always returns a specific instance.
  * Optional, you can specify the extension classes for which you want singletons.
  *
  * @author Decebal Suiu
+ * @author Ajith Kumar
  */
 public class SingletonExtensionFactory extends DefaultExtensionFactory {
 
     private final List<String> extensionClassNames;
 
-    private Map<String, Object> cache;
+    private Map<ClassLoader, Map<String, Object>> cache;
 
     public SingletonExtensionFactory(String... extensionClassNames) {
         this.extensionClassNames = Arrays.asList(extensionClassNames);
 
-        cache = new HashMap<>(); // simple cache implementation
+        cache = new WeakHashMap<>(); // simple cache implementation
     }
 
     @Override
     @SuppressWarnings("unchecked")
     public <T> T create(Class<T> extensionClass) {
         String extensionClassName = extensionClass.getName();
-        if (cache.containsKey(extensionClassName)) {
-            return (T) cache.get(extensionClassName);
+        ClassLoader extensionClassLoader = extensionClass.getClassLoader();
+
+        if (!cache.containsKey(extensionClassLoader)) {
+            cache.put(extensionClassLoader, new HashMap<>());
+        }
+
+        Map<String, Object> classLoaderBucket = cache.get(extensionClassLoader);
+
+        if (classLoaderBucket.containsKey(extensionClassName)) {
+            return (T) classLoaderBucket.get(extensionClassName);
         }
 
         T extension = super.create(extensionClass);
         if (extensionClassNames.isEmpty() || extensionClassNames.contains(extensionClassName)) {
-            cache.put(extensionClassName, extension);
+            classLoaderBucket.put(extensionClassName, extension);
         }
 
         return extension;
index 80e7ab0bc837cc8784609096554967064a4b4717..ac8a3c5a81160b85e92eab8b0cfcb756d03c44ef 100644 (file)
@@ -19,11 +19,17 @@ import org.junit.jupiter.api.Test;
 import org.pf4j.plugin.FailTestExtension;
 import org.pf4j.plugin.TestExtension;
 
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
 import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertSame;
 
 /**
  * @author Decebal Suiu
+ * @author Ajith Kumar
  */
 public class SingletonExtensionFactoryTest {
 
@@ -43,4 +49,38 @@ public class SingletonExtensionFactoryTest {
         assertNotSame(extensionOne, extensionTwo);
     }
 
+    @Test
+    @SuppressWarnings("unchecked")
+    public void createNewEachTimeFromDifferentClassLoaders() throws Exception {
+        ExtensionFactory extensionFactory = new SingletonExtensionFactory();
+
+        // Get classpath locations
+        URL[] classpathReferences = getClasspathReferences();
+
+        // Create different classloaders for the classpath references and load classes respectively
+        ClassLoader klassLoaderOne = new URLClassLoader(classpathReferences, null);
+        Class klassOne = klassLoaderOne.loadClass(TestExtension.class.getName());
+        ClassLoader klassLoaderTwo = new URLClassLoader(classpathReferences, null);
+        Class klassTwo = klassLoaderTwo.loadClass(TestExtension.class.getName());
+
+        // create instances
+        Object instanceOne = extensionFactory.create(klassOne);
+        Object instanceTwo = extensionFactory.create(klassTwo);
+
+        // assert that instances not same
+        assertNotSame(instanceOne, instanceTwo);
+    }
+
+    private URL[] getClasspathReferences() throws MalformedURLException {
+        String classpathProperty = System.getProperty("java.class.path");
+
+        String[] classpaths = classpathProperty.split(":");
+        URL[] uris = new URL[classpaths.length];
+
+        for (int index = 0; index < classpaths.length; index++) {
+            uris[index] = new File(classpaths[index]).toURI().toURL();
+        }
+        return uris;
+    }
+
 }