@@ -60,6 +60,26 @@ | |||
<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> |
@@ -15,10 +15,6 @@ | |||
*/ | |||
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); |
@@ -15,20 +15,26 @@ | |||
*/ | |||
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") |
@@ -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> |