<version>${asm.version}</version> | <version>${asm.version}</version> | ||||
<optional>true</optional> | <optional>true</optional> | ||||
</dependency> | </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> | <dependency> | ||||
<groupId>junit</groupId> | <groupId>junit</groupId> | ||||
<artifactId>junit</artifactId> | <artifactId>junit</artifactId> |
*/ | */ | ||||
package org.pf4j; | package org.pf4j; | ||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.pf4j.util.StringUtils; | |||||
import java.io.Closeable; | import java.io.Closeable; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | 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} | * This class implements the boilerplate plugin code that any {@link PluginManager} | ||||
private PluginDescriptorFinder pluginDescriptorFinder; | private PluginDescriptorFinder pluginDescriptorFinder; | ||||
/* | |||||
/** | |||||
* A map of plugins this manager is responsible for (the key is the 'pluginId'). | * A map of plugins this manager is responsible for (the key is the 'pluginId'). | ||||
*/ | */ | ||||
protected Map<String, PluginWrapper> plugins; | protected Map<String, PluginWrapper> plugins; | ||||
/* | |||||
/** | |||||
* A map of plugin class loaders (the key is the 'pluginId'). | * A map of plugin class loaders (the key is the 'pluginId'). | ||||
*/ | */ | ||||
private Map<String, ClassLoader> pluginClassLoaders; | private Map<String, ClassLoader> pluginClassLoaders; | ||||
/* | |||||
/** | |||||
* A list with unresolved plugins (unresolved dependency). | * A list with unresolved plugins (unresolved dependency). | ||||
*/ | */ | ||||
private List<PluginWrapper> unresolvedPlugins; | private List<PluginWrapper> unresolvedPlugins; | ||||
*/ | */ | ||||
private List<PluginWrapper> resolvedPlugins; | private List<PluginWrapper> resolvedPlugins; | ||||
/* | |||||
/** | |||||
* A list with started plugins. | * A list with started plugins. | ||||
*/ | */ | ||||
private List<PluginWrapper> startedPlugins; | private List<PluginWrapper> startedPlugins; | ||||
/* | |||||
/** | |||||
* The registered {@link PluginStateListener}s. | * The registered {@link PluginStateListener}s. | ||||
*/ | */ | ||||
private List<PluginStateListener> pluginStateListeners; | private List<PluginStateListener> pluginStateListeners; | ||||
/* | |||||
/** | |||||
* Cache value for the runtime mode. | * Cache value for the runtime mode. | ||||
* No need to re-read it because it wont change at runtime. | * No need to re-read it because it wont change at runtime. | ||||
*/ | */ | ||||
private RuntimeMode runtimeMode; | private RuntimeMode runtimeMode; | ||||
/* | |||||
/** | |||||
* The system version used for comparisons to the plugin requires attribute. | * The system version used for comparisons to the plugin requires attribute. | ||||
*/ | */ | ||||
private String systemVersion = "0.0.0"; | private String systemVersion = "0.0.0"; | ||||
} | } | ||||
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { | protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { | ||||
// test for plugin duplication | |||||
// Test for plugin path duplication | |||||
String pluginId = idForPath(pluginPath); | String pluginId = idForPath(pluginPath); | ||||
if (pluginId != null) { | if (pluginId != null) { | ||||
throw new PluginAlreadyLoadedException(pluginId, pluginPath); | throw new PluginAlreadyLoadedException(pluginId, pluginPath); | ||||
} | } | ||||
// retrieves the plugin descriptor | |||||
// Retrieve and validate the plugin descriptor | |||||
PluginDescriptorFinder pluginDescriptorFinder = getPluginDescriptorFinder(); | PluginDescriptorFinder pluginDescriptorFinder = getPluginDescriptorFinder(); | ||||
log.debug("Use '{}' to find plugins descriptors", pluginDescriptorFinder); | log.debug("Use '{}' to find plugins descriptors", pluginDescriptorFinder); | ||||
log.debug("Finding plugin descriptor for plugin '{}'", pluginPath); | log.debug("Finding plugin descriptor for plugin '{}'", pluginPath); | ||||
PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginPath); | PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginPath); | ||||
validatePluginDescriptor(pluginDescriptor); | 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); | log.debug("Found descriptor {}", pluginDescriptor); | ||||
String pluginClassName = pluginDescriptor.getPluginClass(); | String pluginClassName = pluginDescriptor.getPluginClass(); | ||||
log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); | log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); |
*/ | */ | ||||
package org.pf4j; | 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.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | import static org.junit.Assert.assertFalse; | ||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
import static org.junit.Assert.assertThat; | |||||
import static org.junit.Assert.assertTrue; | 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 { | public class LoadPluginsTest { | ||||
assertNull(pluginManager.loadPluginFromPath(pluginZip.path())); | 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 | @Test | ||||
public void loadUnloadLoad() throws Exception { | public void loadUnloadLoad() throws Exception { | ||||
PluginZip pluginZip = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | PluginZip pluginZip = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") |
<asm.version>7.0</asm.version> | <asm.version>7.0</asm.version> | ||||
<junit.version>4.12</junit.version> | <junit.version>4.12</junit.version> | ||||
<hamcrest.version>2.1</hamcrest.version> | |||||
<mockito.version>2.24.0</mockito.version> | <mockito.version>2.24.0</mockito.version> | ||||
<cobertura.version>2.7</cobertura.version> | <cobertura.version>2.7</cobertura.version> | ||||
<coveralls.version>3.1.0</coveralls.version> | <coveralls.version>3.1.0</coveralls.version> |