mirror of https://github.com/halo-dev/halo
Support obtaining plugins root in plugins (#6269)
#### 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 支持在插件中获取插件根目录 ```pull/6271/head
parent
36fb44c8b7
commit
ad66247872
|
@ -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<Path> {
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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<Path> createPluginsRoot() {
|
||||
var pluginsRoot = pluginProperties.getPluginsRoot();
|
||||
if (StringUtils.isNotBlank(pluginsRoot)) {
|
||||
return List.of(Paths.get(pluginsRoot));
|
||||
}
|
||||
return super.createPluginsRoot();
|
||||
return List.of(pluginsRootGetter.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -34,8 +34,6 @@ spring:
|
|||
|
||||
halo:
|
||||
work-dir: ${user.home}/.halo2
|
||||
plugin:
|
||||
plugins-root: ${halo.work-dir}/plugins
|
||||
attachment:
|
||||
resource-mappings:
|
||||
- pathPattern: /upload/**
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue