diff --git a/api/src/main/java/run/halo/app/plugin/PluginConfigUpdatedEvent.java b/api/src/main/java/run/halo/app/plugin/PluginConfigUpdatedEvent.java
new file mode 100644
index 000000000..77877d0ca
--- /dev/null
+++ b/api/src/main/java/run/halo/app/plugin/PluginConfigUpdatedEvent.java
@@ -0,0 +1,32 @@
+package run.halo.app.plugin;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.util.Map;
+import lombok.Builder;
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+import run.halo.app.core.extension.Plugin;
+import run.halo.app.extension.ConfigMap;
+
+/**
+ *
Event that is triggered when the {@link ConfigMap } represented by
+ * {@link Plugin.PluginSpec#getConfigMapName()} in the {@link Plugin} is updated.
+ * has two properties, oldConfig and newConfig, which represent the {@link ConfigMap#getData()}
+ * property value of the {@link ConfigMap}.
+ *
+ * @author guqing
+ * @since 2.17.0
+ */
+@Getter
+public class PluginConfigUpdatedEvent extends ApplicationEvent {
+ private final Map oldConfig;
+ private final Map newConfig;
+
+ @Builder
+ public PluginConfigUpdatedEvent(Object source, Map oldConfig,
+ Map newConfig) {
+ super(source);
+ this.oldConfig = oldConfig;
+ this.newConfig = newConfig;
+ }
+}
diff --git a/api/src/main/java/run/halo/app/plugin/PluginContext.java b/api/src/main/java/run/halo/app/plugin/PluginContext.java
index 01bae2f96..d5fa54989 100644
--- a/api/src/main/java/run/halo/app/plugin/PluginContext.java
+++ b/api/src/main/java/run/halo/app/plugin/PluginContext.java
@@ -1,5 +1,6 @@
package run.halo.app.plugin;
+import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.pf4j.RuntimeMode;
@@ -17,10 +18,13 @@ import org.pf4j.RuntimeMode;
* @since 2.10.0
*/
@Getter
+@Builder
@RequiredArgsConstructor
public class PluginContext {
private final String name;
+ private final String configMapName;
+
private final String version;
private final RuntimeMode runtimeMode;
diff --git a/application/src/main/java/run/halo/app/plugin/DefaultPluginApplicationContextFactory.java b/application/src/main/java/run/halo/app/plugin/DefaultPluginApplicationContextFactory.java
index c4cc430f0..d77916c48 100644
--- a/application/src/main/java/run/halo/app/plugin/DefaultPluginApplicationContextFactory.java
+++ b/application/src/main/java/run/halo/app/plugin/DefaultPluginApplicationContextFactory.java
@@ -1,5 +1,6 @@
package run.halo.app.plugin;
+import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_SINGLETON;
import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
import java.io.IOException;
@@ -101,10 +102,9 @@ public class DefaultPluginApplicationContextFactory implements PluginApplication
rootContext.getBeanProvider(ReactiveExtensionClient.class)
.ifUnique(client -> {
- var reactiveSettingFetcher = new DefaultReactiveSettingFetcher(client, pluginId);
- var settingFetcher = new DefaultSettingFetcher(reactiveSettingFetcher);
- beanFactory.registerSingleton("reactiveSettingFetcher", reactiveSettingFetcher);
- beanFactory.registerSingleton("settingFetcher", settingFetcher);
+ context.registerBean("reactiveSettingFetcher",
+ DefaultReactiveSettingFetcher.class, bhd -> bhd.setScope(SCOPE_SINGLETON));
+ beanFactory.registerSingleton("settingFetcher", DefaultSettingFetcher.class);
});
rootContext.getBeanProvider(PluginRequestMappingHandlerMapping.class)
diff --git a/application/src/main/java/run/halo/app/plugin/DefaultPluginGetter.java b/application/src/main/java/run/halo/app/plugin/DefaultPluginGetter.java
new file mode 100644
index 000000000..02d9018ac
--- /dev/null
+++ b/application/src/main/java/run/halo/app/plugin/DefaultPluginGetter.java
@@ -0,0 +1,29 @@
+package run.halo.app.plugin;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+import run.halo.app.core.extension.Plugin;
+import run.halo.app.extension.ExtensionClient;
+import run.halo.app.infra.exception.NotFoundException;
+
+/**
+ * Default implementation of {@link PluginGetter}.
+ *
+ * @author guqing
+ * @since 2.17.0
+ */
+@Component
+@RequiredArgsConstructor
+public class DefaultPluginGetter implements PluginGetter {
+ private final ExtensionClient client;
+
+ @Override
+ public Plugin getPlugin(String name) {
+ if (StringUtils.isBlank(name)) {
+ throw new IllegalArgumentException("Plugin name must not be blank");
+ }
+ return client.fetch(Plugin.class, name)
+ .orElseThrow(() -> new NotFoundException("Plugin not found"));
+ }
+}
diff --git a/application/src/main/java/run/halo/app/plugin/DefaultReactiveSettingFetcher.java b/application/src/main/java/run/halo/app/plugin/DefaultReactiveSettingFetcher.java
index dbb89efb9..3b0ba917d 100644
--- a/application/src/main/java/run/halo/app/plugin/DefaultReactiveSettingFetcher.java
+++ b/application/src/main/java/run/halo/app/plugin/DefaultReactiveSettingFetcher.java
@@ -1,16 +1,30 @@
package run.halo.app.plugin;
+import static run.halo.app.extension.index.query.QueryFactory.equal;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
import reactor.core.publisher.Mono;
-import run.halo.app.core.extension.Plugin;
import run.halo.app.extension.ConfigMap;
+import run.halo.app.extension.DefaultExtensionMatcher;
+import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.ReactiveExtensionClient;
+import run.halo.app.extension.controller.Controller;
+import run.halo.app.extension.controller.ControllerBuilder;
+import run.halo.app.extension.controller.Reconciler;
+import run.halo.app.extension.router.selector.FieldSelector;
import run.halo.app.infra.utils.JsonParseException;
import run.halo.app.infra.utils.JsonUtils;
@@ -20,15 +34,36 @@ import run.halo.app.infra.utils.JsonUtils;
* @author guqing
* @since 2.0.0
*/
-public class DefaultReactiveSettingFetcher implements ReactiveSettingFetcher {
+public class DefaultReactiveSettingFetcher
+ implements ReactiveSettingFetcher, Reconciler, DisposableBean,
+ ApplicationContextAware {
private final ReactiveExtensionClient client;
+ private final ExtensionClient blockingClient;
+
+ private final CacheManager cacheManager;
+
+ /**
+ * The application context of the plugin.
+ */
+ private ApplicationContext applicationContext;
+
private final String pluginName;
- public DefaultReactiveSettingFetcher(ReactiveExtensionClient client, String pluginName) {
+ private final String configMapName;
+
+ private final String cacheName;
+
+ public DefaultReactiveSettingFetcher(PluginContext pluginContext,
+ ReactiveExtensionClient client, ExtensionClient blockingClient,
+ CacheManager cacheManager) {
this.client = client;
- this.pluginName = pluginName;
+ this.pluginName = pluginContext.getName();
+ this.configMapName = pluginContext.getConfigMapName();
+ this.blockingClient = blockingClient;
+ this.cacheManager = cacheManager;
+ this.cacheName = buildCacheKey(pluginName);
}
@Override
@@ -60,26 +95,31 @@ public class DefaultReactiveSettingFetcher implements ReactiveSettingFetcher {
.defaultIfEmpty(JsonNodeFactory.instance.missingNode());
}
- private Mono