mirror of https://github.com/halo-dev/halo
Enable defining Reconciler in plugin (#3789)
#### What type of PR is this? /kind feature /area core /area plugin #### What this PR does / why we need it: This PR adds reconciliation mechanism for plugin. After that, we could define a `Reconciler<Request>` like the following when developing plugin: ```java @Slf4j @Component public class ApplicationReconciler implements Reconciler<Reconciler.Request> { @Override public Result reconcile(Request request) { log.info("Application {} changed.", request); return Result.doNotRetry(); } @Override public Controller setupWith(ControllerBuilder builder) { return builder .extension(new Application()) .workerCount(1) .build(); } } ``` You can reconcile any extensions as needed. Meanwhile, all plugins will be stopped when Halo is shutting down. #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/3783 #### Does this PR introduce a user-facing change? ```release-note 支持在插件中定义 Reconciler ```pull/3744/head
parent
7afb3b8687
commit
11a5807682
|
@ -21,6 +21,7 @@ import org.pf4j.PluginState;
|
||||||
import org.pf4j.PluginStateEvent;
|
import org.pf4j.PluginStateEvent;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
@ -41,7 +42,7 @@ import run.halo.app.plugin.event.HaloPluginStoppedEvent;
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class HaloPluginManager extends DefaultPluginManager
|
public class HaloPluginManager extends DefaultPluginManager
|
||||||
implements ApplicationContextAware, InitializingBean {
|
implements ApplicationContextAware, InitializingBean, DisposableBean {
|
||||||
|
|
||||||
private final Map<String, PluginStartingError> startingErrors = new HashMap<>();
|
private final Map<String, PluginStartingError> startingErrors = new HashMap<>();
|
||||||
|
|
||||||
|
@ -403,5 +404,10 @@ public class HaloPluginManager extends DefaultPluginManager
|
||||||
springComponentsFinder.removeComponentsStorage(pluginId);
|
springComponentsFinder.removeComponentsStorage(pluginId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() throws Exception {
|
||||||
|
stopPlugins();
|
||||||
|
}
|
||||||
// end-region
|
// end-region
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package run.halo.app.plugin;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import run.halo.app.extension.ExtensionClient;
|
||||||
|
import run.halo.app.extension.controller.ControllerManager;
|
||||||
|
import run.halo.app.extension.controller.DefaultControllerManager;
|
||||||
|
import run.halo.app.extension.controller.Reconciler;
|
||||||
|
import run.halo.app.extension.controller.Reconciler.Request;
|
||||||
|
import run.halo.app.plugin.event.HaloPluginBeforeStopEvent;
|
||||||
|
import run.halo.app.plugin.event.HaloPluginStartedEvent;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class PluginControllerManager {
|
||||||
|
|
||||||
|
private final Map<String, ControllerManager> controllerManagerMap;
|
||||||
|
|
||||||
|
private final ExtensionClient client;
|
||||||
|
|
||||||
|
public PluginControllerManager(ExtensionClient client) {
|
||||||
|
this.client = client;
|
||||||
|
controllerManagerMap = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void onPluginStarted(HaloPluginStartedEvent event) {
|
||||||
|
var plugin = event.getPlugin();
|
||||||
|
|
||||||
|
var controllerManager = controllerManagerMap.computeIfAbsent(plugin.getPluginId(),
|
||||||
|
id -> new DefaultControllerManager(client));
|
||||||
|
|
||||||
|
getReconcilers(plugin.getPluginId())
|
||||||
|
.forEach(controllerManager::start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void onPluginBeforeStop(HaloPluginBeforeStopEvent event) {
|
||||||
|
// remove controller manager
|
||||||
|
var plugin = event.getPlugin();
|
||||||
|
var controllerManager = controllerManagerMap.remove(plugin.getPluginId());
|
||||||
|
if (controllerManager != null) {
|
||||||
|
// stop all reconcilers
|
||||||
|
getReconcilers(plugin.getPluginId())
|
||||||
|
.forEach(controllerManager::stop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<Reconciler<Request>> getReconcilers(String pluginId) {
|
||||||
|
var context = ExtensionContextRegistry.getInstance().getByPluginId(pluginId);
|
||||||
|
return context.<Reconciler<Request>>getBeanProvider(
|
||||||
|
ResolvableType.forClassWithGenerics(Reconciler.class, Request.class))
|
||||||
|
.orderedStream();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue