From ad66247872582a59ffef56014ea65d60113ab443 Mon Sep 17 00:00:00 2001 From: John Niang Date: Thu, 4 Jul 2024 21:36:33 +0800 Subject: [PATCH] Support obtaining plugins root in plugins (#6269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind feature /kind api-change /area core /area plugin #### What this PR does / why we need it: This PR supports obtaining plugins root in plugins. Below is an example in plugin: ```java @Component class PluginsRootGetterDemo { private final PluginsRootGetter pluginsRootGetter; PluginsRootGetterDemo(PluginsRootGetter pluginsRootGetter) { this.pluginsRootGetter = pluginsRootGetter; } } ``` Meanwhile, I remove the `PluginProperties#pluginsRoot` for a clear way to obtain plugins root. #### Does this PR introduce a user-facing change? ```release-note 支持在插件中获取插件根目录 ``` --- .../halo/app/plugin/PluginsRootGetter.java | 14 +++++++++ .../service/impl/PluginServiceImpl.java | 15 +++++----- .../halo/app/plugin/HaloPluginManager.java | 13 ++++---- .../app/plugin/PluginAutoConfiguration.java | 7 +++-- .../run/halo/app/plugin/PluginProperties.java | 6 ---- .../app/plugin/PluginsRootGetterImpl.java | 28 +++++++++++++++++ .../SharedApplicationContextFactory.java | 4 +++ .../src/main/resources/application.yaml | 2 -- .../endpoint/PluginEndpointTest.java | 6 ---- .../service/impl/PluginServiceImplTest.java | 11 ++++--- ...ltPluginApplicationContextFactoryTest.java | 3 +- .../app/plugin/PluginsRootGetterImplTest.java | 30 +++++++++++++++++++ 12 files changed, 101 insertions(+), 38 deletions(-) create mode 100644 api/src/main/java/run/halo/app/plugin/PluginsRootGetter.java create mode 100644 application/src/main/java/run/halo/app/plugin/PluginsRootGetterImpl.java create mode 100644 application/src/test/java/run/halo/app/plugin/PluginsRootGetterImplTest.java diff --git a/api/src/main/java/run/halo/app/plugin/PluginsRootGetter.java b/api/src/main/java/run/halo/app/plugin/PluginsRootGetter.java new file mode 100644 index 000000000..214d2867c --- /dev/null +++ b/api/src/main/java/run/halo/app/plugin/PluginsRootGetter.java @@ -0,0 +1,14 @@ +package run.halo.app.plugin; + +import java.nio.file.Path; +import java.util.function.Supplier; + +/** + * An interface to get the root path of plugins. + * + * @author johnniang + * @since 2.18.0 + */ +public interface PluginsRootGetter extends Supplier { + +} diff --git a/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java b/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java index be5d5bd06..e1ab070fd 100644 --- a/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java +++ b/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.time.Clock; import java.time.Duration; import java.util.ArrayList; @@ -61,8 +60,8 @@ import run.halo.app.infra.exception.UnsatisfiedAttributeValueException; import run.halo.app.infra.utils.FileUtils; import run.halo.app.infra.utils.VersionUtils; import run.halo.app.plugin.PluginConst; -import run.halo.app.plugin.PluginProperties; import run.halo.app.plugin.PluginUtils; +import run.halo.app.plugin.PluginsRootGetter; import run.halo.app.plugin.SpringPluginManager; import run.halo.app.plugin.YamlPluginDescriptorFinder; import run.halo.app.plugin.YamlPluginFinder; @@ -79,7 +78,7 @@ public class PluginServiceImpl implements PluginService, InitializingBean, Dispo private final SystemVersionSupplier systemVersion; - private final PluginProperties pluginProperties; + private final PluginsRootGetter pluginsRootGetter; private final SpringPluginManager pluginManager; @@ -93,11 +92,13 @@ public class PluginServiceImpl implements PluginService, InitializingBean, Dispo private Clock clock = Clock.systemUTC(); - public PluginServiceImpl(ReactiveExtensionClient client, SystemVersionSupplier systemVersion, - PluginProperties pluginProperties, SpringPluginManager pluginManager) { + public PluginServiceImpl(ReactiveExtensionClient client, + SystemVersionSupplier systemVersion, + PluginsRootGetter pluginsRootGetter, + SpringPluginManager pluginManager) { this.client = client; this.systemVersion = systemVersion; - this.pluginProperties = pluginProperties; + this.pluginsRootGetter = pluginsRootGetter; this.pluginManager = pluginManager; this.jsBundleCache = new BundleCache(".js"); @@ -424,7 +425,7 @@ public class PluginServiceImpl implements PluginService, InitializingBean, Dispo return Mono.fromCallable( () -> { var fileName = PluginUtils.generateFileName(plugin); - var pluginRoot = Paths.get(pluginProperties.getPluginsRoot()); + var pluginRoot = pluginsRootGetter.get(); try { Files.createDirectories(pluginRoot); } catch (IOException e) { diff --git a/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java b/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java index 1dafb4b45..8d0fb1068 100644 --- a/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java +++ b/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java @@ -1,12 +1,10 @@ package run.halo.app.plugin; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Stack; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.pf4j.CompoundPluginLoader; import org.pf4j.CompoundPluginRepository; import org.pf4j.DefaultPluginManager; @@ -46,13 +44,16 @@ public class HaloPluginManager extends DefaultPluginManager implements SpringPlu private final PluginProperties pluginProperties; + private final PluginsRootGetter pluginsRootGetter; + public HaloPluginManager(ApplicationContext rootContext, PluginProperties pluginProperties, - SystemVersionSupplier systemVersionSupplier) { + SystemVersionSupplier systemVersionSupplier, PluginsRootGetter pluginsRootGetter) { this.pluginProperties = pluginProperties; this.rootContext = rootContext; // We have to initialize share context lazily because the root context has not refreshed this.sharedContext = Lazy.of(() -> SharedApplicationContextFactory.create(rootContext)); + this.pluginsRootGetter = pluginsRootGetter; super.runtimeMode = pluginProperties.getRuntimeMode(); setExactVersionAllowed(pluginProperties.isExactVersionAllowed()); @@ -124,11 +125,7 @@ public class HaloPluginManager extends DefaultPluginManager implements SpringPlu @Override protected List createPluginsRoot() { - var pluginsRoot = pluginProperties.getPluginsRoot(); - if (StringUtils.isNotBlank(pluginsRoot)) { - return List.of(Paths.get(pluginsRoot)); - } - return super.createPluginsRoot(); + return List.of(pluginsRootGetter.get()); } @Override diff --git a/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java b/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java index 2fec70b5f..d9a98710a 100644 --- a/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java +++ b/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java @@ -45,8 +45,11 @@ public class PluginAutoConfiguration { @Bean public SpringPluginManager pluginManager(ApplicationContext context, SystemVersionSupplier systemVersionSupplier, - PluginProperties pluginProperties) { - return new HaloPluginManager(context, pluginProperties, systemVersionSupplier); + PluginProperties pluginProperties, + PluginsRootGetter pluginsRootGetter) { + return new HaloPluginManager( + context, pluginProperties, systemVersionSupplier, pluginsRootGetter + ); } @Bean diff --git a/application/src/main/java/run/halo/app/plugin/PluginProperties.java b/application/src/main/java/run/halo/app/plugin/PluginProperties.java index db2844305..9f88115f1 100644 --- a/application/src/main/java/run/halo/app/plugin/PluginProperties.java +++ b/application/src/main/java/run/halo/app/plugin/PluginProperties.java @@ -60,10 +60,4 @@ public class PluginProperties { */ private RuntimeMode runtimeMode = RuntimeMode.DEPLOYMENT; - /** - * Plugin root directory: default “plugins”; when non-jar mode plugin, the value should be an - * absolute directory address. - */ - private String pluginsRoot; - } diff --git a/application/src/main/java/run/halo/app/plugin/PluginsRootGetterImpl.java b/application/src/main/java/run/halo/app/plugin/PluginsRootGetterImpl.java new file mode 100644 index 000000000..7c37a327c --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/PluginsRootGetterImpl.java @@ -0,0 +1,28 @@ +package run.halo.app.plugin; + +import java.nio.file.Path; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import run.halo.app.infra.properties.HaloProperties; + +/** + * Default implementation of {@link PluginsRootGetter}. + * + * @author johnniang + */ +@Component +public class PluginsRootGetterImpl implements PluginsRootGetter { + + private final HaloProperties haloProperties; + + public PluginsRootGetterImpl(HaloProperties haloProperties) { + this.haloProperties = haloProperties; + } + + @Override + @NonNull + public Path get() { + return haloProperties.getWorkDir().resolve("plugins"); + } + +} diff --git a/application/src/main/java/run/halo/app/plugin/SharedApplicationContextFactory.java b/application/src/main/java/run/halo/app/plugin/SharedApplicationContextFactory.java index b195aa4ec..2840991b2 100644 --- a/application/src/main/java/run/halo/app/plugin/SharedApplicationContextFactory.java +++ b/application/src/main/java/run/halo/app/plugin/SharedApplicationContextFactory.java @@ -62,6 +62,10 @@ public enum SharedApplicationContextFactory { rootContext.getBean(CacheManager.class)); beanFactory.registerSingleton("loginHandlerEnhancer", rootContext.getBean(LoginHandlerEnhancer.class)); + rootContext.getBeanProvider(PluginsRootGetter.class) + .ifUnique(pluginsRootGetter -> + beanFactory.registerSingleton("pluginsRootGetter", pluginsRootGetter) + ); // TODO add more shared instance here sharedContext.refresh(); diff --git a/application/src/main/resources/application.yaml b/application/src/main/resources/application.yaml index 4d3d82bf9..a24b43d73 100644 --- a/application/src/main/resources/application.yaml +++ b/application/src/main/resources/application.yaml @@ -34,8 +34,6 @@ spring: halo: work-dir: ${user.home}/.halo2 - plugin: - plugins-root: ${halo.work-dir}/plugins attachment: resource-mappings: - pathPattern: /upload/** diff --git a/application/src/test/java/run/halo/app/core/extension/endpoint/PluginEndpointTest.java b/application/src/test/java/run/halo/app/core/extension/endpoint/PluginEndpointTest.java index 5bfe03569..75ec310c4 100644 --- a/application/src/test/java/run/halo/app/core/extension/endpoint/PluginEndpointTest.java +++ b/application/src/test/java/run/halo/app/core/extension/endpoint/PluginEndpointTest.java @@ -55,15 +55,11 @@ import run.halo.app.extension.Metadata; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.infra.SystemVersionSupplier; import run.halo.app.infra.utils.FileUtils; -import run.halo.app.plugin.PluginProperties; @Slf4j @ExtendWith(MockitoExtension.class) class PluginEndpointTest { - @Mock - PluginProperties pluginProperties; - @Mock private ReactiveExtensionClient client; @@ -221,8 +217,6 @@ class PluginEndpointTest { lenient().when(systemVersionSupplier.get()).thenReturn(Version.valueOf("0.0.0")); tempDirectory = Files.createTempDirectory("halo-test-plugin-upgrade-"); - lenient().when(pluginProperties.getPluginsRoot()) - .thenReturn(tempDirectory.resolve("plugins").toString()); plugin002 = tempDirectory.resolve("plugin-0.0.2.jar"); var plugin002Uri = requireNonNull( diff --git a/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java b/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java index 6918aba6e..2e79ce0fe 100644 --- a/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java +++ b/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java @@ -58,7 +58,7 @@ import run.halo.app.infra.SystemVersionSupplier; import run.halo.app.infra.exception.PluginAlreadyExistsException; import run.halo.app.infra.utils.FileUtils; import run.halo.app.plugin.PluginConst; -import run.halo.app.plugin.PluginProperties; +import run.halo.app.plugin.PluginsRootGetter; import run.halo.app.plugin.SpringPluginManager; import run.halo.app.plugin.YamlPluginFinder; @@ -72,7 +72,7 @@ class PluginServiceImplTest { ReactiveExtensionClient client; @Mock - PluginProperties pluginProperties; + PluginsRootGetter pluginsRootGetter; @Mock SpringPluginManager pluginManager; @@ -123,9 +123,6 @@ class PluginServiceImplTest { getClass().getClassLoader().getResource("plugin/plugin-0.0.2")).toURI(); FileUtils.jar(Paths.get(fakePluingUri), tempDirectory.resolve("plugin-0.0.2.jar")); - lenient().when(pluginProperties.getPluginsRoot()) - .thenReturn(tempDirectory.resolve("plugins").toString()); - lenient().when(systemVersionSupplier.get()).thenReturn(Version.valueOf("0.0.0")); } @@ -144,6 +141,7 @@ class PluginServiceImplTest { @Test void installWhenPluginNotExist() { + when(pluginsRootGetter.get()).thenReturn(tempDirectory.resolve("plugins")); when(client.fetch(Plugin.class, "fake-plugin")).thenReturn(Mono.empty()); var createdPlugin = mock(Plugin.class); when(client.create(isA(Plugin.class))).thenReturn(Mono.just(createdPlugin)); @@ -154,7 +152,6 @@ class PluginServiceImplTest { verify(client).fetch(Plugin.class, "fake-plugin"); verify(systemVersionSupplier).get(); - verify(pluginProperties).getPluginsRoot(); verify(client).create(isA(Plugin.class)); } @@ -181,6 +178,8 @@ class PluginServiceImplTest { @Test void upgradeNormally() { + when(pluginsRootGetter.get()).thenReturn(tempDirectory.resolve("plugins")); + var oldFakePlugin = createPlugin("fake-plugin", plugin -> { plugin.getSpec().setEnabled(true); plugin.getSpec().setVersion("0.0.1"); diff --git a/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java b/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java index 74e56a65a..0f37a595e 100644 --- a/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java +++ b/application/src/test/java/run/halo/app/plugin/DefaultPluginApplicationContextFactoryTest.java @@ -22,7 +22,7 @@ class DefaultPluginApplicationContextFactoryTest { @BeforeEach void setUp() { - factory = new DefaultPluginApplicationContextFactory((SpringPluginManager) pluginManager); + factory = new DefaultPluginApplicationContextFactory(pluginManager); } @Test @@ -41,6 +41,7 @@ class DefaultPluginApplicationContextFactoryTest { assertInstanceOf(PluginApplicationContext.class, context); assertNotNull(context.getBeanProvider(SearchService.class).getIfUnique()); + assertNotNull(context.getBeanProvider(PluginsRootGetter.class).getIfUnique()); // TODO Add more assertions here. } diff --git a/application/src/test/java/run/halo/app/plugin/PluginsRootGetterImplTest.java b/application/src/test/java/run/halo/app/plugin/PluginsRootGetterImplTest.java new file mode 100644 index 000000000..68744cbf5 --- /dev/null +++ b/application/src/test/java/run/halo/app/plugin/PluginsRootGetterImplTest.java @@ -0,0 +1,30 @@ +package run.halo.app.plugin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import run.halo.app.infra.properties.HaloProperties; + +@ExtendWith(MockitoExtension.class) +class PluginsRootGetterImplTest { + + @Mock + HaloProperties haloProperties; + + @InjectMocks + PluginsRootGetterImpl pluginsRootGetter; + + @Test + void shouldGetterPluginsRootCorrectly() { + var haloWorkDir = Paths.get("halo-work-dir"); + when(haloProperties.getWorkDir()).thenReturn(haloWorkDir); + assertEquals(haloWorkDir.resolve("plugins"), pluginsRootGetter.get()); + } + +}