]> source.dussan.org Git - pf4j.git/commitdiff
Check no plugin with same pluginId is loaded (#287)
authorDmitry Timofeev <dmitry-timofeev@users.noreply.github.com>
Sat, 2 Mar 2019 20:10:05 +0000 (22:10 +0200)
committerDecebal Suiu <decebal.suiu@gmail.com>
Sat, 2 Mar 2019 20:10:05 +0000 (22:10 +0200)
pf4j/pom.xml
pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
pf4j/src/test/java/org/pf4j/LoadPluginsTest.java
pom.xml

index a1be7a48bf62f88140f830a42818f9731bac4d78..061412dd6bfb42d0ced6589a1851f936ed80c387 100644 (file)
             <version>${asm.version}</version>
             <optional>true</optional>
         </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>${hamcrest.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <!-- An empty artifact, required while JUnit 4 is on the classpath to override its
+                 dependency on hamcrest.
+
+                 See http://hamcrest.org/JavaHamcrest/distributables#upgrading-from-hamcrest-1x
+                 -->
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <version>${hamcrest.version}</version>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
index 22cf7e367115f8ab33e9c7392dc7f86ee3d6cf3c..c17fdab5d376f00b32191c6c62b6aa70bea0f2a0 100644 (file)
  */
 package org.pf4j;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.pf4j.util.StringUtils;
-
 import java.io.Closeable;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -31,6 +27,9 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.pf4j.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * This class implements the boilerplate plugin code that any {@link PluginManager}
@@ -51,17 +50,17 @@ public abstract class AbstractPluginManager implements PluginManager {
 
     private PluginDescriptorFinder pluginDescriptorFinder;
 
-    /*
+    /**
      * A map of plugins this manager is responsible for (the key is the 'pluginId').
      */
     protected Map<String, PluginWrapper> plugins;
 
-    /*
+    /**
      * A map of plugin class loaders (the key is the 'pluginId').
      */
     private Map<String, ClassLoader> pluginClassLoaders;
 
-    /*
+    /**
      * A list with unresolved plugins (unresolved dependency).
      */
     private List<PluginWrapper> unresolvedPlugins;
@@ -71,23 +70,23 @@ public abstract class AbstractPluginManager implements PluginManager {
      */
     private List<PluginWrapper> resolvedPlugins;
 
-    /*
+    /**
      * A list with started plugins.
      */
     private List<PluginWrapper> startedPlugins;
 
-    /*
+    /**
      * The registered {@link PluginStateListener}s.
      */
     private List<PluginStateListener> pluginStateListeners;
 
-    /*
+    /**
      * Cache value for the runtime mode.
      * No need to re-read it because it wont change at runtime.
      */
     private RuntimeMode runtimeMode;
 
-    /*
+    /**
      * The system version used for comparisons to the plugin requires attribute.
      */
     private String systemVersion = "0.0.0";
@@ -822,18 +821,31 @@ public abstract class AbstractPluginManager implements PluginManager {
     }
 
     protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException {
-        // test for plugin duplication
+        // Test for plugin path duplication
         String pluginId = idForPath(pluginPath);
         if (pluginId != null) {
             throw new PluginAlreadyLoadedException(pluginId, pluginPath);
         }
 
-        // retrieves the plugin descriptor
+        // Retrieve and validate the plugin descriptor
         PluginDescriptorFinder pluginDescriptorFinder = getPluginDescriptorFinder();
         log.debug("Use '{}' to find plugins descriptors", pluginDescriptorFinder);
         log.debug("Finding plugin descriptor for plugin '{}'", pluginPath);
         PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginPath);
         validatePluginDescriptor(pluginDescriptor);
+
+        // Check there are no loaded plugins with the retrieved id
+        pluginId = pluginDescriptor.getPluginId();
+        if (plugins.containsKey(pluginId)) {
+            PluginWrapper loadedPlugin = getPlugin(pluginId);
+            throw new PluginException("There is an already loaded plugin ({}) "
+                    + "with the same id ({}) as the plugin at path '{}'. Simultaneous loading "
+                    + "of plugins with the same PluginId is not currently supported.\n"
+                    + "As a workaround you may include PluginVersion and PluginProvider "
+                    + "in PluginId.",
+                loadedPlugin, pluginId, pluginPath);
+        }
+
         log.debug("Found descriptor {}", pluginDescriptor);
         String pluginClassName = pluginDescriptor.getPluginClass();
         log.debug("Class '{}' for plugin '{}'",  pluginClassName, pluginPath);
index 8b6af11a95dbe8bede4a384931c4debe3b1da82b..c2917e8280c08103d1dbe3b1b5a1e554a2c97a59 100644 (file)
  */
 package org.pf4j;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.pf4j.plugin.PluginZip;
-
-import java.nio.file.Files;
-import java.nio.file.Paths;
-
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.startsWith;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.pf4j.plugin.PluginZip;
 
 public class LoadPluginsTest {
 
@@ -73,6 +79,75 @@ public class LoadPluginsTest {
         assertNull(pluginManager.loadPluginFromPath(pluginZip.path()));
     }
 
+    @Test
+    public void loadPluginWithSameIdDifferentPathFails() throws Exception {
+        String pluginId = "myPlugin";
+        String pluginVersion = "1.2.3";
+        File plugin1Path = pluginsFolder.newFile("my-plugin-1.2.3.zip");
+        PluginZip plugin1 = new PluginZip.Builder(plugin1Path, pluginId)
+            .pluginVersion(pluginVersion)
+            .build();
+
+        File plugin2Path = pluginsFolder.newFile("my-plugin-1.2.3-renamed.zip");
+        PluginZip plugin2 = new PluginZip.Builder(plugin2Path, pluginId)
+            .pluginVersion(pluginVersion)
+            .build();
+
+        // Verify the first plugin with the given id is loaded
+        assertNotNull(pluginManager.loadPluginFromPath(plugin1.path()));
+        Path loadedPlugin1Path = pluginManager.getPlugin(pluginId).getPluginPath();
+
+        try {
+            // Verify the second plugin is not loaded as it has the same metadata
+            pluginManager.loadPluginFromPath(plugin2.path());
+            fail("Expected loadPluginFromPath to fail");
+        } catch (PluginException e) {
+            // Check the path of the loaded plugin remains the same
+            PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId);
+            assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path));
+            // Check the message includes relevant information
+            String message = e.getMessage();
+            assertThat(message, startsWith("There is an already loaded plugin"));
+            assertThat(message, containsString(pluginId));
+            assertThat(message, containsString("my-plugin-1.2.3-renamed"));
+        }
+    }
+
+    /**
+     * This test verifies the behaviour as of PF4J 2.x, where plugins of different
+     * versions but with the pluginId cannot be loaded correctly because the API
+     * uses pluginId as the unique identifier of the loaded plugin.
+     */
+    @Test
+    public void loadPluginWithSameIdDifferentVersionsFails() throws Exception {
+        String pluginId = "myPlugin";
+        String plugin1Version = "1.2.3";
+        File plugin1Path = pluginsFolder.newFile("my-plugin-1.2.3.zip");
+        PluginZip plugin1 = new PluginZip.Builder(plugin1Path, pluginId)
+            .pluginVersion(plugin1Version)
+            .build();
+
+        String plugin2Version = "2.0.0";
+        File plugin2Path = pluginsFolder.newFile("my-plugin-2.0.0.zip");
+        PluginZip plugin2 = new PluginZip.Builder(plugin2Path, pluginId)
+            .pluginVersion(plugin2Version)
+            .build();
+
+        // Verify the first plugin with the given id is loaded
+        assertNotNull(pluginManager.loadPluginFromPath(plugin1.path()));
+        Path loadedPlugin1Path = pluginManager.getPlugin(pluginId).getPluginPath();
+        try {
+            // Verify the second plugin is not loaded as it has the same pluginId
+            pluginManager.loadPluginFromPath(plugin2.path());
+            fail("Expected loadPluginFromPath to fail");
+        } catch (PluginException e) {
+            // Check the path and version of the loaded plugin remain the same
+            PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId);
+            assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path));
+            assertThat(loadedPlugin.getDescriptor().getVersion(), equalTo(plugin1Version));
+        }
+    }
+
     @Test
     public void loadUnloadLoad() throws Exception {
         PluginZip pluginZip = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin")
diff --git a/pom.xml b/pom.xml
index 6de3eef6d95f8213aa30b322c0b42282ce3be7f0..dd40519b8fb0ba53525926637c3a9ac5b90d990d 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -48,6 +48,7 @@
         <asm.version>7.0</asm.version>
 
         <junit.version>4.12</junit.version>
+        <hamcrest.version>2.1</hamcrest.version>
         <mockito.version>2.24.0</mockito.version>
         <cobertura.version>2.7</cobertura.version>
         <coveralls.version>3.1.0</coveralls.version>