diff --git a/src/main/java/com/serliunx/ddns/ManagerLite.java b/src/main/java/com/serliunx/ddns/ManagerLite.java index b1f0d0d..9fc9d6e 100644 --- a/src/main/java/com/serliunx/ddns/ManagerLite.java +++ b/src/main/java/com/serliunx/ddns/ManagerLite.java @@ -3,6 +3,8 @@ package com.serliunx.ddns; import com.serliunx.ddns.config.CommandLineConfiguration; import com.serliunx.ddns.config.Configuration; import com.serliunx.ddns.config.PropertiesConfiguration; +import com.serliunx.ddns.config.listener.IpRefreshIntervalListener; +import com.serliunx.ddns.config.listener.NotificationConfigListener; import com.serliunx.ddns.constant.SystemConstants; import com.serliunx.ddns.core.context.FileInstanceContext; import com.serliunx.ddns.core.context.MultipleSourceInstanceContext; @@ -71,6 +73,9 @@ public final class ManagerLite { // 系统初始化 initSystem(); + // 配置监听器初始化 + initConfigurationListeners(); + // 指令初始化 initCommands(); @@ -112,6 +117,16 @@ public final class ManagerLite { System.exit(0); } + /** + * 配置监听器初始化 + */ + private static void initConfigurationListeners() { + // 配置监听器:IP更新间隔变动 + configuration.addListener(new IpRefreshIntervalListener(systemInitializer.getScheduledProvider())); + // 配置监听器:通知变更 + configuration.addListener(new NotificationConfigListener()); + } + /** * 指令初始化 */ diff --git a/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java b/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java index 229c18d..3535fb0 100644 --- a/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java +++ b/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java @@ -5,10 +5,10 @@ import com.serliunx.ddns.support.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; /** * 配置信息的抽象实现, 定义公共逻辑 @@ -19,9 +19,28 @@ import java.util.concurrent.locks.ReentrantLock; */ public abstract class AbstractConfiguration implements Configuration { + /** + * 日志 + */ protected final Logger log = LoggerFactory.getLogger(this.getClass()); + /** + * 配置值存储 + */ protected final Map valueMap = new LinkedHashMap<>(16); + /** + * 上下文更改锁 + */ protected final Lock contextLock = new ReentrantLock(); + /** + * 监听器 + *
  • 仅初始化时做增删改操作. + */ + protected final Map> listeners = new HashMap<>(16); + + /** + * 监听所有配置键的监听器标识符 + */ + public static final String ALL_KEYS_LISTENERS_TAG = "ALL_KEYS_LISTENERS_TAG"; public AbstractConfiguration() {} @@ -117,8 +136,17 @@ public abstract class AbstractConfiguration implements Configuration { contextLock.lock(); if (!valueMap.containsKey(key)) return false; - valueMap.put(key, String.valueOf(value)); - return true; + String oldVal = valueMap.get(key); + String newVal = String.valueOf(value); + valueMap.put(key, newVal); + + try { + invokeListeners(key, oldVal, newVal); + } catch (Exception e) { + log.warn("监听器执行出现异常 => {}", e.getMessage()); + } + + return true; } finally { contextLock.unlock(); } @@ -128,16 +156,51 @@ public abstract class AbstractConfiguration implements Configuration { public void modify(String key, Object value, boolean createIfAbsent) { try { contextLock.lock(); + boolean invoke = false; + String oldVal = valueMap.get(key); + String newVal = String.valueOf(value); if (!valueMap.containsKey(key)) { - if (createIfAbsent) - valueMap.put(key, String.valueOf(value)); - } else - valueMap.put(key, String.valueOf(value)); + if (createIfAbsent) { + valueMap.put(key, newVal); + invoke = true; + } + } else { + valueMap.put(key, newVal); + invoke = true; + } + + if (!invoke) + return; + try { + invokeListeners(key, oldVal, newVal); + } catch (Exception e) { + log.warn("监听器执行出现异常[CIA] => {}", e.getMessage()); + } } finally { contextLock.unlock(); } } + @Override + public void addListener(ConfigListener listener) { + Collection keys = listener.interestedIn(); + Assert.notNull(keys); + if (keys.isEmpty()) { + listeners.computeIfAbsent(ALL_KEYS_LISTENERS_TAG, key -> new ArrayList<>()) + .add(listener); + } else { + keys.forEach(k -> { + listeners.computeIfAbsent(k, k1 -> new ArrayList<>()) + .add(listener); + }); + } + } + + @Override + public Map> getListeners() { + return null; + } + @Override public int getPriority() { return Integer.MAX_VALUE; @@ -177,4 +240,22 @@ public abstract class AbstractConfiguration implements Configuration { * 载入逻辑 */ protected abstract void load0(); + + /** + * 触发监听器 + */ + private void invokeListeners(String key, Object oldVal, Object newVal) throws Exception { + // 触发监听了所有配置项的监听器 + List all = listeners.get(ALL_KEYS_LISTENERS_TAG); + for (ConfigListener cl : all) { + cl.onChanged(this, key, oldVal, newVal); + } + // 触发其他监听器 + List listenerList = listeners.get(key); + if (listenerList == null || listenerList.isEmpty()) + return; + for (ConfigListener cl : listenerList) { + cl.onChanged(this, key, oldVal, newVal); + } + } } diff --git a/src/main/java/com/serliunx/ddns/config/ConfigListener.java b/src/main/java/com/serliunx/ddns/config/ConfigListener.java new file mode 100644 index 0000000..6ae16e3 --- /dev/null +++ b/src/main/java/com/serliunx/ddns/config/ConfigListener.java @@ -0,0 +1,36 @@ +package com.serliunx.ddns.config; + +import java.util.Collection; +import java.util.Collections; + +/** + * 配置监听器 + *
  • 针对配置的变动所需要执行的逻辑 + * + * @author SerLiunx + * @version 1.0.4 + * @since 2025/2/3 + */ +@FunctionalInterface +public interface ConfigListener { + + /** + * 指定当前监听器所感兴趣的配置项(可多个) + *
  • 为空时即监听所有配置项 + * + * @return 感兴趣的配置项 + */ + default Collection interestedIn() { + return Collections.emptyList(); + } + + /** + * 配置项发生了变动的回调 + * + * @param configuration 配置 + * @param key 配置键 + * @param oldVal 旧值 + * @param newVal 新值 + */ + void onChanged(Configuration configuration, String key, Object oldVal, Object newVal) throws Exception; +} diff --git a/src/main/java/com/serliunx/ddns/config/Configuration.java b/src/main/java/com/serliunx/ddns/config/Configuration.java index d56c820..cbd9ce8 100644 --- a/src/main/java/com/serliunx/ddns/config/Configuration.java +++ b/src/main/java/com/serliunx/ddns/config/Configuration.java @@ -3,6 +3,7 @@ package com.serliunx.ddns.config; import com.serliunx.ddns.core.Priority; import com.serliunx.ddns.core.Refreshable; +import java.util.List; import java.util.Map; /** @@ -119,4 +120,18 @@ public interface Configuration extends Refreshable, Priority { * @param createIfAbsent 是否在不存在指定键时创建 */ void modify(String key, Object value, boolean createIfAbsent); + + /** + * 添加配置监听器 + * + * @param listener 监听器 + */ + void addListener(ConfigListener listener); + + /** + * 获取所有配置监听器 + * + * @return 所有监听器 + */ + Map> getListeners(); } diff --git a/src/main/java/com/serliunx/ddns/config/listener/IpRefreshIntervalListener.java b/src/main/java/com/serliunx/ddns/config/listener/IpRefreshIntervalListener.java new file mode 100644 index 0000000..b15c41a --- /dev/null +++ b/src/main/java/com/serliunx/ddns/config/listener/IpRefreshIntervalListener.java @@ -0,0 +1,45 @@ +package com.serliunx.ddns.config.listener; + +import com.serliunx.ddns.config.ConfigListener; +import com.serliunx.ddns.config.Configuration; +import com.serliunx.ddns.constant.ConfigurationKeys; +import com.serliunx.ddns.support.Assert; +import com.serliunx.ddns.support.ipprovider.ScheduledProvider; + +import java.util.Collection; +import java.util.Collections; + +/** + * 配置监听器:IP更新间隔变动 + *
  • 刷新间隔发生变更时通知定时器结束并重新开始计时. + * + * @author SerLiunx + * @version 1.0.4 + * @since 2025/2/3 + */ +public final class IpRefreshIntervalListener implements ConfigListener { + + private final ScheduledProvider scheduledProvider; + + public IpRefreshIntervalListener(ScheduledProvider scheduledProvider) { + Assert.notNull(scheduledProvider); + this.scheduledProvider = scheduledProvider; + } + + @Override + public Collection interestedIn() { + return Collections.singletonList(ConfigurationKeys.KEY_TASK_REFRESH_INTERVAL_IP); + } + + @Override + public void onChanged(Configuration configuration, String key, Object oldVal, Object newVal) throws Exception { + if (key == null + || !key.equals(ConfigurationKeys.KEY_TASK_REFRESH_INTERVAL_IP) + || oldVal == null + || newVal == null + || oldVal.equals(newVal)) + return; + Long newInterval = configuration.getLong(ConfigurationKeys.KEY_TASK_REFRESH_INTERVAL_IP); + scheduledProvider.changeTimePeriod(newInterval); + } +} diff --git a/src/main/java/com/serliunx/ddns/config/listener/NotificationConfigListener.java b/src/main/java/com/serliunx/ddns/config/listener/NotificationConfigListener.java new file mode 100644 index 0000000..9faf08b --- /dev/null +++ b/src/main/java/com/serliunx/ddns/config/listener/NotificationConfigListener.java @@ -0,0 +1,25 @@ +package com.serliunx.ddns.config.listener; + +import com.serliunx.ddns.config.ConfigListener; +import com.serliunx.ddns.config.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 配置监听器:通知变更 + *
  • 仅输出变更信息 + * + * @author SerLiunx + * @version 1.0.4 + * @since 2025/2/3 + */ +public final class NotificationConfigListener implements ConfigListener { + + private static final Logger log = LoggerFactory.getLogger(NotificationConfigListener.class); + + @Override + public void onChanged(Configuration configuration, String key, Object oldVal, Object newVal) throws Exception { + if (log.isDebugEnabled()) + log.debug("配置更新: 配置项 {} 由 {} 调整至 {}", key, oldVal, newVal); + } +}