Browse Source

Fix #520

pull/565/head
Decebal Suiu 10 months ago
parent
commit
30f415fe2d
  1. 75
      pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
  2. 86
      pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java
  3. 25
      pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java

75
pf4j/src/main/java/org/pf4j/AbstractPluginManager.java

@ -91,7 +91,7 @@ public abstract class AbstractPluginManager implements PluginManager {
/** /**
* 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;
@ -455,6 +455,9 @@ public abstract class AbstractPluginManager implements PluginManager {
} 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()));
} }
} }
} }
@ -477,18 +480,11 @@ public abstract class AbstractPluginManager implements PluginManager {
protected PluginState stopPlugin(String pluginId, boolean stopDependents) { protected PluginState stopPlugin(String pluginId, boolean stopDependents) {
checkPluginId(pluginId); checkPluginId(pluginId);
PluginWrapper pluginWrapper = getPlugin(pluginId); // test for started plugin
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); if (!checkPluginState(pluginId, PluginState.STARTED)) {
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) {
// 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) {
@ -500,14 +496,15 @@ public abstract class AbstractPluginManager implements PluginManager {
} }
} }
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;
} }
/** /**
@ -522,6 +519,16 @@ public abstract class AbstractPluginManager implements PluginManager {
} }
} }
/**
* 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);
@ -532,20 +539,21 @@ public abstract class AbstractPluginManager implements PluginManager {
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); pluginStatusProvider.disablePlugin(pluginId);
log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor)); log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor));
return true; return true;
}
return false;
} }
@Override @Override
@ -812,10 +820,9 @@ public abstract class AbstractPluginManager implements PluginManager {
*/ */
protected void resolvePlugins() { protected void resolvePlugins() {
// retrieves the plugins descriptors // retrieves the plugins descriptors
List<PluginDescriptor> descriptors = new ArrayList<>(); List<PluginDescriptor> descriptors = plugins.values().stream()
for (PluginWrapper plugin : plugins.values()) { .map(PluginWrapper::getDescriptor)
descriptors.add(plugin.getDescriptor()); .collect(Collectors.toList());
}
DependencyResolver.Result result = dependencyResolver.resolve(descriptors); DependencyResolver.Result result = dependencyResolver.resolve(descriptors);
@ -1022,6 +1029,16 @@ public abstract class AbstractPluginManager implements PluginManager {
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());

86
pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java

@ -15,28 +15,54 @@
*/ */
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()));
@ -49,8 +75,62 @@ public class AbstractPluginManagerTest {
@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;
}
} }

25
pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java

@ -28,6 +28,8 @@ import java.nio.file.Path;
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;
@ -90,7 +92,7 @@ public class DefaultPluginManagerTest {
@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");
@ -107,7 +109,7 @@ public class DefaultPluginManagerTest {
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");
@ -127,7 +129,7 @@ public class DefaultPluginManagerTest {
/** /**
* 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 {
@ -190,4 +192,21 @@ public class DefaultPluginManagerTest {
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()));
}
} }

Loading…
Cancel
Save