/** | /** | ||||
* 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 won't change at runtime. | |||||
*/ | */ | ||||
protected RuntimeMode runtimeMode; | protected RuntimeMode runtimeMode; | ||||
} catch (PluginRuntimeException e) { | } catch (PluginRuntimeException e) { | ||||
log.error(e.getMessage(), e); | log.error(e.getMessage(), e); | ||||
} | } | ||||
} else { | |||||
// do nothing | |||||
log.debug("Plugin '{}' is not started, nothing to stop", getPluginLabel(pluginWrapper.getDescriptor())); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
protected PluginState stopPlugin(String pluginId, boolean stopDependents) { | protected PluginState stopPlugin(String pluginId, boolean stopDependents) { | ||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | |||||
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); | |||||
PluginState pluginState = pluginWrapper.getPluginState(); | |||||
if (PluginState.STOPPED == pluginState) { | |||||
log.debug("Already stopped plugin '{}'", getPluginLabel(pluginDescriptor)); | |||||
return PluginState.STOPPED; | |||||
} | |||||
// test for disabled plugin | |||||
if (PluginState.DISABLED == pluginState) { | |||||
// test for started plugin | |||||
if (!checkPluginState(pluginId, PluginState.STARTED)) { | |||||
// do nothing | // do nothing | ||||
return pluginState; | |||||
log.debug("Plugin '{}' is not started, nothing to stop", getPluginLabel(pluginId)); | |||||
return getPlugin(pluginId).getPluginState(); | |||||
} | } | ||||
if (stopDependents) { | if (stopDependents) { | ||||
} | } | ||||
} | } | ||||
log.info("Stop plugin '{}'", getPluginLabel(pluginDescriptor)); | |||||
log.info("Stop plugin '{}'", getPluginLabel(pluginId)); | |||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | |||||
pluginWrapper.getPlugin().stop(); | pluginWrapper.getPlugin().stop(); | ||||
pluginWrapper.setPluginState(PluginState.STOPPED); | pluginWrapper.setPluginState(PluginState.STOPPED); | ||||
startedPlugins.remove(pluginWrapper); | |||||
getStartedPlugins().remove(pluginWrapper); | |||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); | |||||
return pluginWrapper.getPluginState(); | |||||
return PluginState.STOPPED; | |||||
} | } | ||||
/** | /** | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Check if the plugin state is equals with the value passed for parameter {@code pluginState}. | |||||
* | |||||
* @param pluginId the pluginId to check | |||||
* @return {@code true} if the plugin state is equals with the value passed for parameter {@code pluginState}, otherwise {@code false} | |||||
*/ | |||||
protected boolean checkPluginState(String pluginId, PluginState pluginState) { | |||||
return getPlugin(pluginId).getPluginState() == pluginState; | |||||
} | |||||
@Override | @Override | ||||
public boolean disablePlugin(String pluginId) { | public boolean disablePlugin(String pluginId) { | ||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
if (PluginState.DISABLED == pluginState) { | if (PluginState.DISABLED == pluginState) { | ||||
log.debug("Already disabled plugin '{}'", getPluginLabel(pluginDescriptor)); | log.debug("Already disabled plugin '{}'", getPluginLabel(pluginDescriptor)); | ||||
return true; | return true; | ||||
} else if (PluginState.STARTED == pluginState) { | |||||
if (PluginState.STOPPED == stopPlugin(pluginId)) { | |||||
log.error("Failed to stop plugin '{}' on disable", getPluginLabel(pluginDescriptor)); | |||||
return false; | |||||
} | |||||
} | } | ||||
if (PluginState.STOPPED == stopPlugin(pluginId)) { | |||||
pluginWrapper.setPluginState(PluginState.DISABLED); | |||||
pluginWrapper.setPluginState(PluginState.DISABLED); | |||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); | |||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||||
pluginStatusProvider.disablePlugin(pluginId); | |||||
log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor)); | |||||
pluginStatusProvider.disablePlugin(pluginId); | |||||
log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor)); | |||||
return true; | |||||
} | |||||
return false; | |||||
return true; | |||||
} | } | ||||
@Override | @Override | ||||
*/ | */ | ||||
protected void resolvePlugins() { | protected void resolvePlugins() { | ||||
// retrieves the plugins descriptors | // retrieves the plugins descriptors | ||||
List<PluginDescriptor> descriptors = new ArrayList<>(); | |||||
for (PluginWrapper plugin : plugins.values()) { | |||||
descriptors.add(plugin.getDescriptor()); | |||||
} | |||||
List<PluginDescriptor> descriptors = plugins.values().stream() | |||||
.map(PluginWrapper::getDescriptor) | |||||
.collect(Collectors.toList()); | |||||
DependencyResolver.Result result = dependencyResolver.resolve(descriptors); | DependencyResolver.Result result = dependencyResolver.resolve(descriptors); | ||||
return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getVersion(); | return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getVersion(); | ||||
} | } | ||||
/** | |||||
* Shortcut for {@code getPluginLabel(getPlugin(pluginId).getDescriptor())}. | |||||
* | |||||
* @param pluginId the pluginId | |||||
* @return the plugin label | |||||
*/ | |||||
protected String getPluginLabel(String pluginId) { | |||||
return getPluginLabel(getPlugin(pluginId).getDescriptor()); | |||||
} | |||||
@SuppressWarnings("unchecked") | @SuppressWarnings("unchecked") | ||||
protected <T> List<Class<? extends T>> getExtensionClasses(List<ExtensionWrapper<T>> extensionsWrapper) { | protected <T> List<Class<? extends T>> getExtensionClasses(List<ExtensionWrapper<T>> extensionsWrapper) { | ||||
List<Class<? extends T>> extensionClasses = new ArrayList<>(extensionsWrapper.size()); | List<Class<? extends T>> extensionClasses = new ArrayList<>(extensionsWrapper.size()); |
*/ | */ | ||||
package org.pf4j; | package org.pf4j; | ||||
import org.junit.jupiter.api.AfterEach; | |||||
import org.junit.jupiter.api.BeforeEach; | |||||
import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||
import org.junit.jupiter.api.io.TempDir; | |||||
import org.pf4j.test.TestExtension; | import org.pf4j.test.TestExtension; | ||||
import org.pf4j.test.TestExtensionPoint; | import org.pf4j.test.TestExtensionPoint; | ||||
import java.io.IOException; | |||||
import java.nio.file.Path; | |||||
import java.nio.file.Paths; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | |||||
import java.util.List; | import java.util.List; | ||||
import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
import static org.junit.jupiter.api.Assertions.assertNotEquals; | import static org.junit.jupiter.api.Assertions.assertNotEquals; | ||||
import static org.junit.jupiter.api.Assertions.assertSame; | |||||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||||
import static org.mockito.Mockito.CALLS_REAL_METHODS; | import static org.mockito.Mockito.CALLS_REAL_METHODS; | ||||
import static org.mockito.Mockito.doNothing; | |||||
import static org.mockito.Mockito.doReturn; | |||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.never; | |||||
import static org.mockito.Mockito.verify; | |||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
import static org.mockito.Mockito.withSettings; | |||||
/** | /** | ||||
* @author Decebal Suiu | * @author Decebal Suiu | ||||
*/ | */ | ||||
public class AbstractPluginManagerTest { | public class AbstractPluginManagerTest { | ||||
private AbstractPluginManager pluginManager; | |||||
@BeforeEach | |||||
void setUp() { | |||||
pluginManager = mock(AbstractPluginManager.class, withSettings() | |||||
.useConstructor() | |||||
.defaultAnswer(CALLS_REAL_METHODS)); | |||||
} | |||||
@AfterEach | |||||
void tearDown() { | |||||
pluginManager = null; | |||||
} | |||||
@Test | @Test | ||||
public void getExtensionsByType() { | public void getExtensionsByType() { | ||||
AbstractPluginManager pluginManager = mock(AbstractPluginManager.class, CALLS_REAL_METHODS); | |||||
ExtensionFinder extensionFinder = mock(ExtensionFinder.class); | ExtensionFinder extensionFinder = mock(ExtensionFinder.class); | ||||
List<ExtensionWrapper<TestExtensionPoint>> extensionList = new ArrayList<>(1); | List<ExtensionWrapper<TestExtensionPoint>> extensionList = new ArrayList<>(1); | ||||
extensionList.add(new ExtensionWrapper<>(new ExtensionDescriptor(0, TestExtension.class), new DefaultExtensionFactory())); | extensionList.add(new ExtensionWrapper<>(new ExtensionDescriptor(0, TestExtension.class), new DefaultExtensionFactory())); | ||||
List<TestExtensionPoint> extensions = pluginManager.getExtensions(TestExtensionPoint.class); | List<TestExtensionPoint> extensions = pluginManager.getExtensions(TestExtensionPoint.class); | ||||
assertEquals(1, extensions.size()); | assertEquals(1, extensions.size()); | ||||
} | } | ||||
@Test | @Test | ||||
public void getVersion() { | public void getVersion() { | ||||
AbstractPluginManager pluginManager = mock(AbstractPluginManager.class, CALLS_REAL_METHODS); | |||||
assertNotEquals("0.0.0", pluginManager.getVersion()); | assertNotEquals("0.0.0", pluginManager.getVersion()); | ||||
} | } | ||||
@Test | |||||
void stopStartedPlugin() throws IOException { | |||||
String pluginId = "plugin1"; | |||||
PluginWrapper pluginWrapper = createPluginWrapper(pluginId); | |||||
pluginWrapper.setPluginState(PluginState.STARTED); | |||||
doReturn(pluginWrapper).when(pluginManager).getPlugin(pluginId); | |||||
doNothing().when(pluginManager).checkPluginId(pluginId); | |||||
doReturn(new ArrayList<>(Arrays.asList(pluginWrapper))).when(pluginManager).getStartedPlugins(); | |||||
PluginState pluginState = pluginManager.stopPlugin(pluginId, false); | |||||
verify(pluginWrapper.getPlugin()).stop(); | |||||
assertSame(PluginState.STOPPED, pluginState); | |||||
} | |||||
@Test | |||||
void stopCreatedPlugin() { | |||||
String pluginId = "plugin1"; | |||||
PluginWrapper pluginWrapper = createPluginWrapper(pluginId); | |||||
doReturn(pluginWrapper).when(pluginManager).getPlugin(pluginId); | |||||
doNothing().when(pluginManager).checkPluginId(pluginId); | |||||
doReturn(new ArrayList<>(Arrays.asList(pluginWrapper))).when(pluginManager).getStartedPlugins(); | |||||
PluginState pluginState = pluginManager.stopPlugin(pluginId, false); | |||||
verify(pluginWrapper.getPlugin(), never()).stop(); | |||||
assertSame(PluginState.CREATED, pluginState); | |||||
} | |||||
@Test | |||||
void checkExistedPluginId() { | |||||
String pluginId = "plugin1"; | |||||
PluginWrapper pluginWrapper = createPluginWrapper(pluginId); | |||||
pluginManager.plugins.put("plugin1", pluginWrapper); | |||||
pluginManager.checkPluginId("plugin1"); | |||||
} | |||||
@Test | |||||
void checkNotExistedPluginId() { | |||||
assertThrows(IllegalArgumentException.class, () -> pluginManager.checkPluginId("plugin1")); | |||||
} | |||||
private PluginWrapper createPluginWrapper(String pluginId) { | |||||
DefaultPluginDescriptor pluginDescriptor = new DefaultPluginDescriptor(); | |||||
pluginDescriptor.setPluginId(pluginId); | |||||
pluginDescriptor.setPluginVersion("1.2.3"); | |||||
PluginWrapper pluginWrapper = new PluginWrapper(pluginManager, pluginDescriptor, Paths.get("plugin1"), getClass().getClassLoader()); | |||||
Plugin plugin= mock(Plugin.class); | |||||
pluginWrapper.setPluginFactory(wrapper -> plugin); | |||||
return pluginWrapper; | |||||
} | |||||
} | } |
import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
import static org.junit.jupiter.api.Assertions.assertFalse; | import static org.junit.jupiter.api.Assertions.assertFalse; | ||||
import static org.junit.jupiter.api.Assertions.assertNotEquals; | |||||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||||
import static org.junit.jupiter.api.Assertions.assertSame; | import static org.junit.jupiter.api.Assertions.assertSame; | ||||
import static org.junit.jupiter.api.Assertions.assertThrows; | import static org.junit.jupiter.api.Assertions.assertThrows; | ||||
import static org.junit.jupiter.api.Assertions.assertTrue; | import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
@Test | @Test | ||||
public void isPluginValid() { | public void isPluginValid() { | ||||
// By default accept all since system version not given | |||||
// By default, accept all since system version not given | |||||
assertTrue(pluginManager.isPluginValid(pluginWrapper)); | assertTrue(pluginManager.isPluginValid(pluginWrapper)); | ||||
pluginManager.setSystemVersion("1.0.0"); | pluginManager.setSystemVersion("1.0.0"); | ||||
public void isPluginValidAllowExact() { | public void isPluginValidAllowExact() { | ||||
pluginManager.setExactVersionAllowed(true); | pluginManager.setExactVersionAllowed(true); | ||||
// By default accept all since system version not given | |||||
// By default, accept all since system version not given | |||||
assertTrue(pluginManager.isPluginValid(pluginWrapper)); | assertTrue(pluginManager.isPluginValid(pluginWrapper)); | ||||
pluginManager.setSystemVersion("1.0.0"); | pluginManager.setSystemVersion("1.0.0"); | ||||
/** | /** | ||||
* Test that a disabled plugin doesn't start. | * Test that a disabled plugin doesn't start. | ||||
* See https://github.com/pf4j/pf4j/issues/223. | |||||
* See <a href="https://github.com/pf4j/pf4j/issues/223">#223</a>. | |||||
*/ | */ | ||||
@Test | @Test | ||||
public void testPluginDisabledNoStart() throws IOException { | public void testPluginDisabledNoStart() throws IOException { | ||||
assertFalse(pluginJar.file().exists()); | assertFalse(pluginJar.file().exists()); | ||||
} | } | ||||
@Test | |||||
public void loadedPluginWithMissingDependencyCanBeUnloaded() throws IOException { | |||||
PluginZip pluginZip = new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.1.1.zip"), "myPlugin") | |||||
.pluginVersion("1.1.1") | |||||
.pluginDependencies("myBasePlugin") | |||||
.build(); | |||||
pluginManager.loadPlugins(); | |||||
pluginManager.startPlugins(); | |||||
PluginWrapper myPlugin = pluginManager.getPlugin(pluginZip.pluginId()); | |||||
assertNotNull(myPlugin); | |||||
assertNotEquals(PluginState.STARTED, myPlugin.getPluginState()); | |||||
assertTrue(pluginManager.unloadPlugin(pluginZip.pluginId())); | |||||
} | |||||
} | } |