From ddd60e4204bfc45ec47489d941ae0608ba736469 Mon Sep 17 00:00:00 2001 From: guqing Date: Wed, 22 Nov 2023 17:46:33 +0800 Subject: [PATCH] fix: OOM occured when using ab to test custom endpoints provided by plugin --- .../plugin/PluginCompositeRouterFunction.java | 19 ++------ .../PluginCustomEndpointInitialization.java | 43 +++++++++++++++++++ .../plugin/event/HaloPluginStartedEvent.java | 9 ++-- .../PluginCompositeRouterFunctionTest.java | 9 +--- 4 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 application/src/main/java/run/halo/app/plugin/PluginCustomEndpointInitialization.java diff --git a/application/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java b/application/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java index dedab890ff..d82b26c670 100644 --- a/application/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java +++ b/application/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java @@ -2,7 +2,7 @@ import static run.halo.app.plugin.ExtensionContextRegistry.getInstance; -import java.util.ArrayList; +import com.google.common.collect.Iterables; import java.util.List; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.lang.NonNull; @@ -14,8 +14,6 @@ import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.halo.app.core.extension.endpoint.CustomEndpoint; -import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder; import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry; /** @@ -48,7 +46,7 @@ public void accept(@NonNull RouterFunctions.Visitor visitor) { } @SuppressWarnings("unchecked") - private List> routerFunctions() { + private Iterable> routerFunctions() { getInstance().acquireReadLock(); try { List contexts = getInstance().getPluginApplicationContexts() @@ -64,18 +62,7 @@ private List> routerFunctions() { .toList(); var reverseProxies = reverseProxyRouterFunctionFactory.getRouterFunctions(); - var endpointBuilder = new CustomEndpointsBuilder(); - contexts.forEach(context -> context.getBeanProvider(CustomEndpoint.class) - .orderedStream() - .forEach(endpointBuilder::add)); - var customEndpoint = endpointBuilder.build(); - - List> routerFunctions = - new ArrayList<>(rawRouterFunctions.size() + reverseProxies.size() + 1); - routerFunctions.addAll(rawRouterFunctions); - routerFunctions.addAll(reverseProxies); - routerFunctions.add(customEndpoint); - return routerFunctions; + return Iterables.concat(rawRouterFunctions, reverseProxies); } finally { getInstance().releaseReadLock(); } diff --git a/application/src/main/java/run/halo/app/plugin/PluginCustomEndpointInitialization.java b/application/src/main/java/run/halo/app/plugin/PluginCustomEndpointInitialization.java new file mode 100644 index 0000000000..df05dfaddf --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/PluginCustomEndpointInitialization.java @@ -0,0 +1,43 @@ +package run.halo.app.plugin; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder; +import run.halo.app.plugin.event.HaloPluginStartedEvent; + +/** + *

An Initialization class for plugin's {@link RouterFunction}.

+ * + * @author guqing + * @since 2.11.0 + */ +@Component +@RequiredArgsConstructor +public class PluginCustomEndpointInitialization { + + private final HaloPluginManager haloPluginManager; + + /** + * Get all {@link CustomEndpoint} beans from plugin's {@link PluginApplicationContext} and + * create a {@link RouterFunction} for them to register to the bean factory on plugin startup. + */ + @Async + @EventListener(HaloPluginStartedEvent.class) + public void onPluginStartUp(HaloPluginStartedEvent event) { + var pluginName = event.getPlugin().getPluginId(); + var applicationContext = haloPluginManager.getPluginApplicationContext(pluginName); + + var endpoints = applicationContext.getBeansOfType(CustomEndpoint.class).values(); + if (endpoints.isEmpty()) { + return; + } + var endpointBuilder = new CustomEndpointsBuilder(); + endpoints.forEach(endpointBuilder::add); + applicationContext.registerBean("pluginCustomEndpointRouter", + RouterFunction.class, endpointBuilder::build); + } +} diff --git a/application/src/main/java/run/halo/app/plugin/event/HaloPluginStartedEvent.java b/application/src/main/java/run/halo/app/plugin/event/HaloPluginStartedEvent.java index 97467e37ed..b2768ab040 100644 --- a/application/src/main/java/run/halo/app/plugin/event/HaloPluginStartedEvent.java +++ b/application/src/main/java/run/halo/app/plugin/event/HaloPluginStartedEvent.java @@ -1,24 +1,23 @@ package run.halo.app.plugin.event; +import lombok.Getter; import org.pf4j.PluginWrapper; import org.springframework.context.ApplicationEvent; +import org.springframework.util.Assert; /** * This event will be published to application context once plugin is started. * * @author guqing */ +@Getter public class HaloPluginStartedEvent extends ApplicationEvent { private final PluginWrapper plugin; - public HaloPluginStartedEvent(Object source, PluginWrapper plugin) { super(source); + Assert.notNull(plugin, "Plugin must not be null."); this.plugin = plugin; } - - public PluginWrapper getPlugin() { - return plugin; - } } diff --git a/application/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java b/application/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java index 88649147e2..3752c5d5d1 100644 --- a/application/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java +++ b/application/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java @@ -24,7 +24,6 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import run.halo.app.core.extension.endpoint.CustomEndpoint; import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry; /** @@ -43,9 +42,6 @@ class PluginCompositeRouterFunctionTest { @Mock ObjectProvider rawRouterFunctionsProvider; - @Mock - ObjectProvider customEndpointsProvider; - @InjectMocks PluginCompositeRouterFunction compositeRouterFunction; @@ -59,11 +55,9 @@ void setUp() { ExtensionContextRegistry.getInstance().register("fake-plugin", fakeContext); when(rawRouterFunctionsProvider.orderedStream()).thenReturn(Stream.empty()); - when(customEndpointsProvider.orderedStream()).thenReturn(Stream.empty()); when(fakeContext.getBeanProvider(RouterFunction.class)) .thenReturn(rawRouterFunctionsProvider); - when(fakeContext.getBeanProvider(CustomEndpoint.class)).thenReturn(customEndpointsProvider); compositeRouterFunction = new PluginCompositeRouterFunction(reverseProxyRouterFunctionRegistry); @@ -93,11 +87,10 @@ void route() { .verify(); verify(rawRouterFunctionsProvider).orderedStream(); - verify(customEndpointsProvider).orderedStream(); } private ServerWebExchange createExchange(String urlTemplate) { return MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate)); } -} \ No newline at end of file +}