]> source.dussan.org Git - pf4j.git/commitdiff
Fix #520
authorDecebal Suiu <decebal.suiu@gmail.com>
Sat, 3 Feb 2024 22:56:27 +0000 (00:56 +0200)
committerDecebal Suiu <decebal.suiu@gmail.com>
Sat, 3 Feb 2024 22:56:27 +0000 (00:56 +0200)
pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java
pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java

index beac7c832d7301c03570a8ebd7c2d606e5cc7228..c3e8c0f8718a2256648a5cb906db96eff380f570 100644 (file)
@@ -91,7 +91,7 @@ public abstract class AbstractPluginManager implements PluginManager {
 
     /**
      * 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;
 
@@ -455,6 +455,9 @@ public abstract class AbstractPluginManager implements PluginManager {
                 } catch (PluginRuntimeException 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) {
         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
-            return pluginState;
+            log.debug("Plugin '{}' is not started, nothing to stop", getPluginLabel(pluginId));
+            return getPlugin(pluginId).getPluginState();
         }
 
         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.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
     public boolean disablePlugin(String pluginId) {
         checkPluginId(pluginId);
@@ -532,20 +539,21 @@ public abstract class AbstractPluginManager implements PluginManager {
         if (PluginState.DISABLED == pluginState) {
             log.debug("Already disabled plugin '{}'", getPluginLabel(pluginDescriptor));
             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
@@ -812,10 +820,9 @@ public abstract class AbstractPluginManager implements PluginManager {
      */
     protected void resolvePlugins() {
         // 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);
 
@@ -1022,6 +1029,16 @@ public abstract class AbstractPluginManager implements PluginManager {
         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")
     protected <T> List<Class<? extends T>> getExtensionClasses(List<ExtensionWrapper<T>> extensionsWrapper) {
         List<Class<? extends T>> extensionClasses = new ArrayList<>(extensionsWrapper.size());
index c43bc635b9bd982d694b990669c7612b8dd6e5d0..70a931290d191c57e91059acf51b6aab36c8f812 100644 (file)
  */
 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.io.TempDir;
 import org.pf4j.test.TestExtension;
 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.Arrays;
 import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 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.doNothing;
+import static org.mockito.Mockito.doReturn;
 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.withSettings;
 
 /**
  * @author Decebal Suiu
  */
 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
     public void getExtensionsByType() {
-        AbstractPluginManager pluginManager = mock(AbstractPluginManager.class, CALLS_REAL_METHODS);
-
         ExtensionFinder extensionFinder = mock(ExtensionFinder.class);
         List<ExtensionWrapper<TestExtensionPoint>> extensionList = new ArrayList<>(1);
         extensionList.add(new ExtensionWrapper<>(new ExtensionDescriptor(0, TestExtension.class), new DefaultExtensionFactory()));
@@ -46,11 +72,65 @@ public class AbstractPluginManagerTest {
         List<TestExtensionPoint> extensions = pluginManager.getExtensions(TestExtensionPoint.class);
         assertEquals(1, extensions.size());
     }
-    
+
     @Test
     public void getVersion() {
-        AbstractPluginManager pluginManager = mock(AbstractPluginManager.class, CALLS_REAL_METHODS);
         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;
+    }
+
 }
index fab1a02ed7eedb51c711b92e733896c6c58be302..3edb1c50240c05f2c6ef01afd68ebd31afad812b 100644 (file)
@@ -28,6 +28,8 @@ import java.nio.file.Path;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 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.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -90,7 +92,7 @@ public class DefaultPluginManagerTest {
 
     @Test
     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));
 
         pluginManager.setSystemVersion("1.0.0");
@@ -107,7 +109,7 @@ public class DefaultPluginManagerTest {
     public void isPluginValidAllowExact() {
         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));
 
         pluginManager.setSystemVersion("1.0.0");
@@ -127,7 +129,7 @@ public class DefaultPluginManagerTest {
 
     /**
      * 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
     public void testPluginDisabledNoStart() throws IOException {
@@ -190,4 +192,21 @@ public class DefaultPluginManagerTest {
         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()));
+    }
+
 }