Application.xml废弃<resources>节点

This commit is contained in:
Redkale
2022-12-09 21:54:51 +08:00
parent 8a630ff007
commit 8c20ba96f6
10 changed files with 833 additions and 478 deletions

View File

@@ -3,49 +3,48 @@ redkale.nodeid = 1000
redkale.port = 6560
redkale.lib = ./
#\u3010resources\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
#\u3010executor\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
redkale.resources.executor.threads = 4
redkale.resources.executor.hash = false
redkale.executor.threads = 4
redkale.executor.hash = false
#\u3010transport\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
redkale.resources.transport.bufferCapacity = 32k
redkale.resources.transport.bufferPoolSize = 32
redkale.transport.bufferCapacity = 32k
redkale.transport.bufferPoolSize = 32
#\u3010excludelibs\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
redkale.resources.excludelibs.value = ^.*mysql.*$;^.*kafka.*$
redkale.excludelibs.value = ^.*mysql.*$;^.*kafka.*$
#\u3010cluster\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
redkale.resources.cluster.type = org.redkalex.cluster.consul.ConsulClusterAgent
redkale.resources.cluster.waits= = false
redkale.resources.cluster.protocols = SNCP
redkale.resources.cluster.ports = 7070;7071
redkale.cluster.type = org.redkalex.cluster.consul.ConsulClusterAgent
redkale.cluster.waits= = false
redkale.cluster.protocols = SNCP
redkale.cluster.ports = 7070;7071
redkale.resources.mq[0].name =
redkale.resources.mq[0].type = org.redkalex.mq.kafka.KafkaMessageAgent
redkale.resources.mq[0].servers.value = 127.0.0.1:9101
redkale.mq[0].name =
redkale.mq[0].type = org.redkalex.mq.kafka.KafkaMessageAgent
redkale.mq[0].servers.value = 127.0.0.1:9101
redkale.resources.group[0].name =
redkale.resources.group[0].protocol = TCP
redkale.resources.group[0].node[0].addr = 127.0.0.1
redkale.resources.group[0].node[0].port = 7070
redkale.group[0].name =
redkale.group[0].protocol = TCP
redkale.group[0].node[0].addr = 127.0.0.1
redkale.group[0].node[0].port = 7070
#\u3010listener\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
redkale.resources.listener.value = org.redkalex.xxx.XXXApplicationListener
redkale.listener[0].value = org.redkalex.xxx.XXXApplicationListener
#\u3010properties\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
redkale.resources.properties.load = config.properties
redkale.resources.properties.property[0].name = system.property.yyyy
redkale.resources.properties.property[0].value = YYYYYY
redkale.resources.properties.property[1].name = xxxxxxx
redkale.resources.properties.property[1].value = YYYYYY
redkale.properties.load = config.properties
redkale.properties.property[0].name = system.property.yyyy
redkale.properties.property[0].value = YYYYYY
redkale.properties.property[1].name = xxxxxxx
redkale.properties.property[1].value = YYYYYY
redkale.server[0].protocol = HTTP
redkale.server[0].host = 127.0.0.1
redkale.server[0].port = 6060
redkale.server[0].root = root
redkale.server[0].lib =
#\u3010\u8282\u70b9\u5728<server>\u4e2d\u552f\u4e00\u3011
#\u3010ssl\u8282\u70b9\u5728<server>\u4e2d\u552f\u4e00\u3011
redkale.server[0].ssl.build = org.redkale.net.SSLBuilder\u5b50\u7c7b
redkale.server[0].services[0].autoload = true

View File

@@ -28,124 +28,124 @@
<application nodeid="1000" port="6560" lib="">
<!--
【节点全局唯一
已废弃,不再需要此节点】
所有服务所需的资源
-->
<resources>
<!--
【节点全局唯一】 @since 2.3.0
全局Serivce执行的线程池 Application.workExecutor, 没配置该节点将自动创建一个。
threads 线程数为0表示不启用workExecutor只用IO线程。默认: CPU核数, 核数=1的情况下默认值为2
hash: 是否使用ThreadHashExecutor作为线程池默认值为false
-->
<executor threads="4" hash="false"/>
<!--
【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒 为0表示无超时限制
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
-->
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
<!--
【节点全局唯一】
自动扫描时排除部分包路径
value 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
-->
<excludelibs value="^.*mysql.*$;^.*kafka.*$"/>
<!--
【节点全局唯一】
第三方服务发现管理接口
type 类名必须是org.redkale.cluster.ClusterAgent的子类
waits: 注销服务后是否需要等待检查周期时间后再进行Service销毁默认值为false
当一个Service进行服务注销后不能立刻销毁Service因为健康检测是有间隔时间差的
需要等待一个健康检测周期时间,让其他进程都更新完服务列表。
如果使用MQ可以设置为false如果对服务健壮性要求高建议设置为true
protocols: 服务发现可以处理的协议, 默认值为: SNCP, 多个协议用分号;隔开
ports: 服务发现可以处理的端口, 多个端口用分号;隔开
ttls: 心跳频率,多少秒一次
xxxx: 自定义的字段属性例如CacheClusterAgent有source字段; ConsulClusterAgent有apiurl字段;
-->
<cluster type="org.redkalex.cluster.consul.ConsulClusterAgent" waits="false" protocols="SNCP" ports="7070;7071" xxx="xxx" />
<!--
MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。
MQ跟着协议走所以mq的属性值需要被赋值在rest节点上, 由于SncpServlet是自动生成的故SNCP协议下mq属性值被赋值在service/services节点上
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
type 实现类名必须是org.redkale.mq.MessageAgent的子类
coder: MessageRecord的解析器类必须是org.redkale.mq.MessageCoder<MessageRecord>的实现类,
可对数据包进行加密解密默认值org.redkale.mq.MessageRecordCoder
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
-->
<mq name="" type="org.redkalex.mq.kafka.KafkaMessageAgent">
<servers value="127.0.0.1:9101"/>
<consumer>
<property name="xxxxxx" value="XXXXXXXX"/>
</consumer>
<producer>
<property name="xxxxxx" value="XXXXXXXX"/>
</producer>
</mq>
<!--
一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
一个group节点对应一个 Transport 对象。
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol 值范围UDP TCP 默认TCP
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。
-->
<group name="" protocol="TCP">
<!--
需要将本地node的addr与port列在此处。
同一个<node>节点值只能存在一个<group>节点内即同一个addr+port只能属于一个group。
addr: required IP地址
port: required 端口
-->
<node addr="127.0.0.1" port="7070"/>
</group>
<!--
Application启动的监听事件,可配置多个节点
value: 类名必须是ApplicationListener的子类
-->
<listener value="org.redkalex.xxx.XXXApplicationListener"/>
<!--
【节点全局唯一】
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
先加载子节点property再加载load文件 最后加载agent的实现子类。
load: 加载文件,多个用;隔开。
其他属性: 供org.redkale.boot.PropertiesAgentProvider使用判断
默认置入的system.property.的有:
System.setProperty("redkale.net.transport.poolmaxconns", "100");
System.setProperty("redkale.net.transport.pinginterval", "30");
System.setProperty("redkale.net.transport.checkinterval", "30");
System.setProperty("redkale.convert.tiny", "true");
System.setProperty("redkale.convert.pool.size", "128");
System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
System.setProperty("redkale.trace.enable", "false");
<properties>节点下也可包含非<property>节点.
非<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
-->
<properties load="config.properties">
<property name="system.property.yyyy" value="YYYYYY"/>
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
</properties>
<resources>
</resources>
<!--
【节点全局唯一】 @since 2.3.0
全局Serivce执行的线程池 Application.workExecutor, 没配置该节点将自动创建一个。
threads 线程数为0表示不启用workExecutor只用IO线程。默认: CPU核数, 核数=1的情况下默认值为2
hash: 是否使用ThreadHashExecutor作为线程池默认值为false
-->
<executor threads="4" hash="false"/>
<!--
【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒 为0表示无超时限制
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
-->
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
<!--
【节点全局唯一】
自动扫描时排除部分包路径
value 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
-->
<excludelibs value="^.*mysql.*$;^.*kafka.*$"/>
<!--
【节点全局唯一】
第三方服务发现管理接口
type 类名必须是org.redkale.cluster.ClusterAgent的子类
waits: 注销服务后是否需要等待检查周期时间后再进行Service销毁默认值为false
当一个Service进行服务注销后不能立刻销毁Service因为健康检测是有间隔时间差的
需要等待一个健康检测周期时间,让其他进程都更新完服务列表。
如果使用MQ可以设置为false如果对服务健壮性要求高建议设置为true
protocols: 服务发现可以处理的协议, 默认值为: SNCP, 多个协议用分号;隔开
ports: 服务发现可以处理的端口, 多个端口用分号;隔开
ttls: 心跳频率,多少秒一次
xxxx: 自定义的字段属性例如CacheClusterAgent有source字段; ConsulClusterAgent有apiurl字段;
-->
<cluster type="org.redkalex.cluster.consul.ConsulClusterAgent" waits="false" protocols="SNCP" ports="7070;7071" xxx="xxx" />
<!--
MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。
MQ跟着协议走所以mq的属性值需要被赋值在rest节点上, 由于SncpServlet是自动生成的故SNCP协议下mq属性值被赋值在service/services节点上
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
type 实现类名必须是org.redkale.mq.MessageAgent的子类
coder: MessageRecord的解析器类必须是org.redkale.mq.MessageCoder<MessageRecord>的实现类,
可对数据包进行加密解密默认值org.redkale.mq.MessageRecordCoder
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
-->
<mq name="" type="org.redkalex.mq.kafka.KafkaMessageAgent">
<servers value="127.0.0.1:9101"/>
<consumer>
<property name="xxxxxx" value="XXXXXXXX"/>
</consumer>
<producer>
<property name="xxxxxx" value="XXXXXXXX"/>
</producer>
</mq>
<!--
一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
一个group节点对应一个 Transport 对象。
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol 值范围UDP TCP 默认TCP
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。
-->
<group name="" protocol="TCP">
<!--
需要将本地node的addr与port列在此处。
同一个<node>节点值只能存在一个<group>节点内即同一个addr+port只能属于一个group。
addr: required IP地址
port: required 端口
-->
<node addr="127.0.0.1" port="7070"/>
</group>
<!--
Application启动的监听事件,可配置多个节点
value: 类名必须是ApplicationListener的子类
-->
<listener value="org.redkalex.xxx.XXXApplicationListener"/>
<!--
【节点全局唯一】
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
先加载子节点property再加载load文件 最后加载agent的实现子类。
load: 加载文件,多个用;隔开。
其他属性: 供org.redkale.boot.PropertiesAgentProvider使用判断
默认置入的system.property.的有:
System.setProperty("redkale.net.transport.poolmaxconns", "100");
System.setProperty("redkale.net.transport.pinginterval", "30");
System.setProperty("redkale.net.transport.checkinterval", "30");
System.setProperty("redkale.convert.tiny", "true");
System.setProperty("redkale.convert.pool.size", "128");
System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
System.setProperty("redkale.trace.enable", "false");
<properties>节点下也可包含非<property>节点.
非<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
-->
<properties load="config.properties">
<property name="system.property.yyyy" value="YYYYYY"/>
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
</properties>
<!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
@@ -366,4 +366,5 @@
<!-- 参数完全同上 -->
<services autoload="true" includes="" excludes="" />
</server>
</application>

View File

@@ -82,11 +82,6 @@ public final class Application {
*/
public static final String RESNAME_APP_CONF_FILE = "APP_CONF_FILE";
/**
* application.xml 文件中resources节点的内容 类型: AnyValue
*/
public static final String RESNAME_APP_GRES = "APP_GRES";
/**
* 当前进程节点的nodeid 类型int
*/
@@ -152,9 +147,6 @@ public final class Application {
//Source 原始的配置资源, 只会存在redkale.datasource(.|[) redkale.cachesource(.|[)开头的配置项
final Properties sourceProperties = new Properties();
//sourceProperties对应的AnyValue类型对象
AnyValue sourceConfig;
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -261,7 +253,7 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_HOME, File.class, root);
this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI());
File confFile = null;
try {
try { //设置APP_HOME
this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
if (System.getProperty(RESNAME_APP_HOME) == null) {
System.setProperty(RESNAME_APP_HOME, root.getCanonicalPath());
@@ -415,7 +407,6 @@ public final class Application {
throw new RuntimeException(e);
}
//------------------------------------ 配置 <transport> 节点 ------------------------------------
final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
String excludelib0 = null;
ClusterAgent cluster = null;
@@ -425,108 +416,106 @@ public final class Application {
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AnyValue executorConf = null;
if (resources != null) {
executorConf = resources.getAnyValue("executor");
AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
if (transportConf != null) {
//--------------transportBufferPool-----------
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 32 * 1024);
readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds);
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Utility.cpus() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
}
executorConf = config.getAnyValue("executor");
AnyValue excludelibConf = config.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = config.getAnyValue("transport");
int groupsize = config.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
if (transportConf != null) {
//--------------transportBufferPool-----------
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 32 * 1024);
readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds);
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Utility.cpus() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
}
AnyValue clusterConf = resources.getAnyValue("cluster");
if (clusterConf != null) {
try {
String classVal = clusterConf.getValue("type", clusterConf.getValue("value")); //兼容value字段
if (classVal == null || classVal.isEmpty() || classVal.indexOf('.') < 0) { //不包含.表示非类名,比如值: consul, nacos
Iterator<ClusterAgentProvider> it = ServiceLoader.load(ClusterAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(ClusterAgentProvider.class);
while (it.hasNext()) {
ClusterAgentProvider provider = it.next();
if (provider != null) RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); //loader class
if (provider != null && provider.acceptsConf(clusterConf)) {
cluster = provider.createInstance();
cluster.setConfig(clusterConf);
break;
}
AnyValue clusterConf = config.getAnyValue("cluster");
if (clusterConf != null) {
try {
String classVal = clusterConf.getValue("type", clusterConf.getValue("value")); //兼容value字段
if (classVal == null || classVal.isEmpty() || classVal.indexOf('.') < 0) { //不包含.表示非类名,比如值: consul, nacos
Iterator<ClusterAgentProvider> it = ServiceLoader.load(ClusterAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(ClusterAgentProvider.class);
while (it.hasNext()) {
ClusterAgentProvider provider = it.next();
if (provider != null) RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); //loader class
if (provider != null && provider.acceptsConf(clusterConf)) {
cluster = provider.createInstance();
cluster.setConfig(clusterConf);
break;
}
if (cluster == null) {
ClusterAgent cacheClusterAgent = new CacheClusterAgent();
if (cacheClusterAgent.acceptsConf(clusterConf)) {
cluster = cacheClusterAgent;
cluster.setConfig(clusterConf);
}
}
if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='type' value error: " + clusterConf);
} else {
Class type = classLoader.loadClass(classVal);
if (!ClusterAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance();
}
if (cluster == null) {
ClusterAgent cacheClusterAgent = new CacheClusterAgent();
if (cacheClusterAgent.acceptsConf(clusterConf)) {
cluster = cacheClusterAgent;
cluster.setConfig(clusterConf);
}
}
//此时不能执行cluster.init因内置的对象可能依赖config.properties配置项
} catch (Exception e) {
logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e);
if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='type' value error: " + clusterConf);
} else {
Class type = classLoader.loadClass(classVal);
if (!ClusterAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance();
cluster.setConfig(clusterConf);
}
}
//此时不能执行cluster.init因内置的对象可能依赖config.properties配置项
} catch (Exception e) {
logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e);
}
}
AnyValue[] mqConfs = resources.getAnyValues("mq");
if (mqConfs != null && mqConfs.length > 0) {
mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[i];
String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
mqnames.add(mqname);
String namex = mqConf.getValue("names");
if (namex != null && !namex.isEmpty()) {
for (String n : namex.split(";")) {
if (n.trim().isEmpty()) continue;
if (mqnames.contains(n.trim())) throw new RuntimeException("mq.name(" + n.trim() + ") is repeat");
mqnames.add(n.trim());
}
AnyValue[] mqConfs = config.getAnyValues("mq");
if (mqConfs != null && mqConfs.length > 0) {
mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[i];
String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
mqnames.add(mqname);
String namex = mqConf.getValue("names");
if (namex != null && !namex.isEmpty()) {
for (String n : namex.split(";")) {
if (n.trim().isEmpty()) continue;
if (mqnames.contains(n.trim())) throw new RuntimeException("mq.name(" + n.trim() + ") is repeat");
mqnames.add(n.trim());
}
try {
String classVal = mqConf.getValue("type", mqConf.getValue("value")); //兼容value字段
if (classVal == null || classVal.isEmpty() || classVal.indexOf('.') < 0) { //不包含.表示非类名,比如值: kafka, pulsar
Iterator<MessageAgentProvider> it = ServiceLoader.load(MessageAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(MessageAgentProvider.class);
while (it.hasNext()) {
MessageAgentProvider provider = it.next();
if (provider != null) RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); //loader class
if (provider != null && provider.acceptsConf(mqConf)) {
mqs[i] = provider.createInstance();
mqs[i].setConfig(mqConf);
break;
}
}
if (mqs[i] == null) logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf);
} else {
Class type = classLoader.loadClass(classVal);
if (!MessageAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance();
}
try {
String classVal = mqConf.getValue("type", mqConf.getValue("value")); //兼容value字段
if (classVal == null || classVal.isEmpty() || classVal.indexOf('.') < 0) { //不包含.表示非类名,比如值: kafka, pulsar
Iterator<MessageAgentProvider> it = ServiceLoader.load(MessageAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(MessageAgentProvider.class);
while (it.hasNext()) {
MessageAgentProvider provider = it.next();
if (provider != null) RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); //loader class
if (provider != null && provider.acceptsConf(mqConf)) {
mqs[i] = provider.createInstance();
mqs[i].setConfig(mqConf);
break;
}
}
//此时不能执行mq.init因内置的对象可能依赖config.properties配置项
} catch (Exception e) {
logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e);
if (mqs[i] == null) logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf);
} else {
Class type = classLoader.loadClass(classVal);
if (!MessageAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance();
mqs[i].setConfig(mqConf);
}
}
//此时不能执行mq.init因内置的对象可能依赖config.properties配置项
} catch (Exception e) {
logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e);
}
}
}
@@ -591,7 +580,8 @@ public final class Application {
}
private void loadResourceProperties() throws IOException {
final String confDir = this.confPath.toString();
final Properties dyncProps = new Properties();
final AtomicInteger propertyIndex = new AtomicInteger();
//------------------------------------ 读取本地DataSource、CacheSource配置 ------------------------------------
if ("file".equals(this.confPath.getScheme())) {
File sourceFile = new File(new File(confPath), "source.properties");
@@ -603,7 +593,9 @@ public final class Application {
props.forEach((key, val) -> {
if (key.toString().startsWith("redkale.datasource.") || key.toString().startsWith("redkale.datasource[")
|| key.toString().startsWith("redkale.cachesource.") || key.toString().startsWith("redkale.cachesource[")) {
sourceProperties.put(key, val);
dyncProps.put(key, val);
} else {
logger.log(Level.WARNING, "skip illegal key " + key + " in source.properties");
}
});
} else {
@@ -612,13 +604,13 @@ public final class Application {
if (persist.isFile() && persist.canRead()) {
logger.log(Level.WARNING, "persistence.xml is deprecated, replaced by source.properties");
InputStream in = new FileInputStream(persist);
sourceProperties.putAll(DataSources.loadSourceProperties(in));
dyncProps.putAll(DataSources.loadSourceProperties(in));
in.close();
}
}
} else { //从url或jar文件中resources读取
try {
final URI sourceURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : confDir, "source.properties");
final URI sourceURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : this.confPath.toString(), "source.properties");
InputStream in = sourceURI.toURL().openStream();
Properties props = new Properties();
props.load(in);
@@ -626,85 +618,184 @@ public final class Application {
props.forEach((key, val) -> {
if (key.toString().startsWith("redkale.datasource.") || key.toString().startsWith("redkale.datasource[")
|| key.toString().startsWith("redkale.cachesource.") || key.toString().startsWith("redkale.cachesource[")) {
sourceProperties.put(key, val);
dyncProps.put(key, val);
} else {
logger.log(Level.WARNING, "skip illegal key " + key + " in source.properties");
}
});
} catch (Exception e) { //没有文件 跳过
}
//兼容 persistence.xml 【已废弃】
try {
final URI xmlURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : confDir, "persistence.xml");
final URI xmlURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : this.confPath.toString(), "persistence.xml");
InputStream in = xmlURI.toURL().openStream();
sourceProperties.putAll(DataSources.loadSourceProperties(in));
dyncProps.putAll(DataSources.loadSourceProperties(in));
in.close();
logger.log(Level.WARNING, "persistence.xml is deprecated, replaced by source.properties");
} catch (Exception e) { //没有文件 跳过
}
}
//------------------------------------ 读取配置项 ------------------------------------
final AnyValue resources = config.getAnyValue("resources");
if (resources != null) {
resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources);
final AnyValue propertiesConf = resources.getAnyValue("properties");
if (propertiesConf != null) {
for (AnyValue prop : propertiesConf.getAnyValues("property")) {
String key = prop.getValue("name");
String value = prop.getValue("value");
if (key == null || value == null) continue;
updateEnvironmentProperty(key, value, null, null);
}
String dfloads = propertiesConf.getValue("load");
if (dfloads != null) {
for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue;
final URI df = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : confDir, dfload.trim());
if (df != null && (!"file".equals(df.getScheme()) || df.toString().contains("!") || new File(df).isFile())) {
Properties ps = new Properties();
try {
InputStream in = df.toURL().openStream();
ps.load(in);
in.close();
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "load properties(" + dfload + ") size = " + ps.size());
ps.forEach((x, y) -> { //load中的配置项除了redkale.cachesource.和redkale.datasource.开头不应该有其他redkale.开头配置项
updateEnvironmentProperty(x.toString(), y, null, null);
});
} catch (Exception e) {
logger.log(Level.WARNING, "load properties(" + dfload + ") error", e);
}
//------------------------------------ 读取配置项 ------------------------------------
AnyValue propertiesConf = config.getAnyValue("properties");
if (propertiesConf == null) {
final AnyValue resources = config.getAnyValue("resources");
if (resources != null) {
logger.log(Level.WARNING, "<resources> in application config file is deprecated");
propertiesConf = resources.getAnyValue("properties");
}
}
if (propertiesConf != null) {
final Properties agentEnvs = new Properties();
if (propertiesConf.getValue("load") != null) { //本地配置项文件加载
for (String dfload : propertiesConf.getValue("load").split(";")) {
if (dfload.trim().isEmpty()) continue;
final URI df = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : this.confPath.toString(), dfload.trim());
if (df != null && (!"file".equals(df.getScheme()) || df.toString().contains("!") || new File(df).isFile())) {
Properties ps = new Properties();
try {
InputStream in = df.toURL().openStream();
ps.load(in);
in.close();
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "load properties(" + dfload + ") size = " + ps.size());
ps.forEach((x, y) -> { //load中的配置项除了redkale.cachesource.和redkale.datasource.开头不应该有其他redkale.开头配置项
if (!x.toString().startsWith("redkale.") && !x.toString().startsWith("property.")) {
agentEnvs.put(x, y);
} else {
logger.log(Level.WARNING, "skip illegal(startswith redkale. or property.) key " + x + " in properties file");
}
});
} catch (Exception e) {
logger.log(Level.WARNING, "load properties(" + dfload + ") error", e);
}
}
}
{ //可能通过系统环境变量配置信息
Iterator<PropertiesAgentProvider> it = ServiceLoader.load(PropertiesAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(PropertiesAgentProvider.class);
List<PropertiesAgentProvider> providers = new ArrayList<>();
while (it.hasNext()) {
PropertiesAgentProvider provider = it.next();
if (provider != null && provider.acceptsConf(propertiesConf)) {
RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName());
providers.add(provider);
}
}
for (PropertiesAgentProvider provider : InstanceProvider.sort(providers)) {
long s = System.currentTimeMillis();
this.propertiesAgent = provider.createInstance();
this.resourceFactory.inject(this.propertiesAgent);
if (compileMode) {
this.propertiesAgent.compile(propertiesConf);
} else {
this.propertiesAgent.init(this, propertiesConf);
}
logger.info("PropertiesAgent (type = " + this.propertiesAgent.getClass().getSimpleName() + ") init in " + (System.currentTimeMillis() - s) + " ms");
break;
}
}
}
final AnyValue[] sourceConfs = resources.getAnyValues("source");
if (sourceConfs != null && sourceConfs.length > 0) {
//<source>节点 【已废弃】
throw new RuntimeException("<source> in application.xml is deprecated, replaced by source.properties");
{ //可能通过系统环境变量配置信息
Iterator<PropertiesAgentProvider> it = ServiceLoader.load(PropertiesAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(PropertiesAgentProvider.class);
List<PropertiesAgentProvider> providers = new ArrayList<>();
while (it.hasNext()) {
PropertiesAgentProvider provider = it.next();
if (provider != null && provider.acceptsConf(propertiesConf)) {
RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName());
providers.add(provider);
}
}
for (PropertiesAgentProvider provider : InstanceProvider.sort(providers)) {
long s = System.currentTimeMillis();
this.propertiesAgent = provider.createInstance();
this.resourceFactory.inject(this.propertiesAgent);
if (compileMode) {
this.propertiesAgent.compile(propertiesConf);
} else {
Properties props = this.propertiesAgent.init(this, propertiesConf);
if (props != null) {
agentEnvs.putAll(props);
}
}
logger.info("PropertiesAgent (type = " + this.propertiesAgent.getClass().getSimpleName() + ") init in " + (System.currentTimeMillis() - s) + " ms");
break;
}
}
final Properties oldEnvs = new Properties();
for (AnyValue prop : propertiesConf.getAnyValues("property")) {
String key = prop.getValue("name");
String value = prop.getValue("value");
if (key == null || value == null) continue;
if (key.startsWith("property.")) {
logger.log(Level.WARNING, "property key (" + key + ") startswith 'property.' is illegal, auto remove the prefix 'property.'");
key = key.substring("property.".length());
}
oldEnvs.put(key, value);
}
agentEnvs.forEach((k, v) -> {
if (k.toString().startsWith("redkale.")) {
dyncProps.put(k, v);
} else if (k.toString().startsWith("property.")) {
logger.log(Level.WARNING, "skip illegal(startswith property.) key " + k + " in remote properties agent");
} else {
oldEnvs.put(k, v); //新配置项会覆盖旧的
}
});
//原有properties节点上的属性同步到dyncEnvs
propertiesConf.forEach((k, v) -> dyncProps.put("redkale.properties." + k, v));
oldEnvs.forEach((k, v) -> { //去重后的配置项
String prefix = "redkale.properties.property[" + propertyIndex.getAndIncrement() + "]";
dyncProps.put(prefix + ".name", k);
dyncProps.put(prefix + ".value", v);
});
//移除旧节点
((DefaultAnyValue) this.config).removeAnyValues("properties");
}
//环境变量的优先级最高
System.getProperties().forEach((k, v) -> {
if (k.toString().startsWith("redkale.executor.") //节点全局唯一
|| k.toString().startsWith("redkale.transport.") //节点全局唯一
|| k.toString().startsWith("redkale.excludelibs.") //节点全局唯一
|| k.toString().startsWith("redkale.cluster.") //节点全局唯一
|| k.toString().startsWith("redkale.mq.")
|| k.toString().startsWith("redkale.mq[")
|| k.toString().startsWith("redkale.group.")
|| k.toString().startsWith("redkale.group[")
|| k.toString().startsWith("redkale.listener.")
|| k.toString().startsWith("redkale.listener[")
|| k.toString().startsWith("redkale.server.")
|| k.toString().startsWith("redkale.server[")) {
dyncProps.put(k, v);
} else if (k.toString().startsWith("redkale.properties.")) {
if (k.toString().startsWith("redkale.properties.property.")
|| k.toString().startsWith("redkale.properties.property[")) {
dyncProps.put(k, v);
} else {
//支持系统变量 -Dredkale.properties.mykey=my-value
String prefix = "redkale.properties.property[" + propertyIndex.getAndIncrement() + "]";
dyncProps.put(prefix + ".name", k.toString().substring("redkale.properties.".length()));
dyncProps.put(prefix + ".value", v);
}
}
});
if (!dyncProps.isEmpty()) {
//合并配置
this.config.merge(AnyValue.loadFromProperties(dyncProps).getAnyValue("redkale"), NodeServer.appConfigmergeFunction);
dyncProps.forEach((key, val) -> {
if (key.toString().startsWith("redkale.datasource.") || key.toString().startsWith("redkale.datasource[")
|| key.toString().startsWith("redkale.cachesource.") || key.toString().startsWith("redkale.cachesource[")) {
if (key.toString().endsWith(".name")) {
logger.log(Level.WARNING, "skip illegal key " + key + " in source config, key cannot endsWith '.name'");
} else {
sourceProperties.put(key, val);
}
}
});
}
//使用合并后的新配置节点
propertiesConf = this.config.getAnyValue("properties");
if (propertiesConf != null) {
//清除property节点数组的下坐标
((DefaultAnyValue) propertiesConf).clearParentArrayIndex("property");
//注入配置项
for (AnyValue prop : propertiesConf.getAnyValues("property")) {
String key = prop.getValue("name");
String value = prop.getValue("value");
if (key == null) continue;
value = value == null ? value : replaceValue(value);
if (key.startsWith("system.property.")) {
String propName = key.substring("system.property.".length());
if (System.getProperty(propName) == null) { //命令行传参数优先级高
System.setProperty(propName, value);
}
} else if (key.startsWith("mimetype.property.")) {
MimeType.add(key.substring("mimetype.property.".length()), value);
} else if (key.startsWith("property.")) {
envProperties.put(key, value);
resourceFactory.register(key, value);
} else {
envProperties.put(key, value);
resourceFactory.register(false, "property." + key, value);
}
}
}
}
@@ -981,23 +1072,13 @@ public final class Application {
}
private AnyValue findSourceConfig(String sourceName, String sourceType) {
if (sourceConfig == null) {
synchronized ((sourceProperties)) {
if (sourceConfig == null) {
sourceConfig = AnyValue.loadFromProperties(sourceProperties);
}
}
}
AnyValue redNode = sourceConfig.getAnyValue("redkale");
if (redNode != null) {
AnyValue sourceNode = redNode.getAnyValue(sourceType);
if (sourceNode != null) {
AnyValue confNode = sourceNode.getAnyValue(sourceName);
if (confNode != null) { //必须要设置name属性
((DefaultAnyValue) confNode).setValue("name", sourceName);
}
return confNode;
AnyValue sourceNode = config.getAnyValue(sourceType);
if (sourceNode != null) {
AnyValue confNode = sourceNode.getAnyValue(sourceName);
if (confNode != null) { //必须要设置name属性
((DefaultAnyValue) confNode).setValue("name", sourceName);
}
return confNode;
}
return null;
}
@@ -1126,35 +1207,31 @@ public final class Application {
}
private void initResources() throws Exception {
//-------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources");
if (resources != null) {
//------------------------------------------------------------------------
for (AnyValue conf : resources.getAnyValues("group")) {
final String group = conf.getValue("name", "");
final String protocol = conf.getValue("protocol", Transport.DEFAULT_NETPROTOCOL).toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, new LinkedHashSet<>());
for (AnyValue node : conf.getAnyValues("node")) {
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
ginfo.putAddress(addr);
}
sncpTransportFactory.addGroupInfo(ginfo);
//------------------------------------------------------------------------
for (AnyValue conf : config.getAnyValues("group")) {
final String group = conf.getValue("name", "");
final String protocol = conf.getValue("protocol", Transport.DEFAULT_NETPROTOCOL).toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
for (AnyValue conf : resources.getAnyValues("listener")) {
final String listenClass = conf.getValue("value", "");
if (listenClass.isEmpty()) continue;
Class clazz = classLoader.loadClass(listenClass);
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
@SuppressWarnings("unchecked")
ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(listener);
listener.init(config);
this.listeners.add(listener);
TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, new LinkedHashSet<>());
for (AnyValue node : conf.getAnyValues("node")) {
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
ginfo.putAddress(addr);
}
sncpTransportFactory.addGroupInfo(ginfo);
}
for (AnyValue conf : config.getAnyValues("listener")) {
final String listenClass = conf.getValue("value", "");
if (listenClass.isEmpty()) continue;
Class clazz = classLoader.loadClass(listenClass);
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
@SuppressWarnings("unchecked")
ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(listener);
listener.init(config);
this.listeners.add(listener);
}
//------------------------------------------------------------------------
}
@@ -1633,7 +1710,12 @@ public final class Application {
}
System.setProperty(RESNAME_APP_CONF_DIR, confDir);
String text = Utility.readThenClose(appConfFile.toURL().openStream());
AnyValue conf = text.trim().startsWith("<") ? AnyValue.loadFromXml(text, (k, v) -> v.replace("${APP_HOME}", home)).getAnyValue("application") : AnyValue.loadFromProperties(text).getAnyValue("redkale");
AnyValue conf;
if (text.trim().startsWith("<")) {
conf = AnyValue.loadFromXml(text, (k, v) -> v.replace("${APP_HOME}", home)).getAnyValue("application");
} else {
conf = AnyValue.loadFromProperties(text).getAnyValue("redkale");
}
if (fromCache) ((DefaultAnyValue) conf).addValue("[config-from-cache]", "true");
return conf;
}
@@ -1712,84 +1794,101 @@ public final class Application {
return value == null ? value : value.replace("${APP_HOME}", homePath).replace("${APP_NAME}", name);
}
//初始化加载时envChangeCache=null
//配置项动态变更时 envChangeCache!=null, 由调用方统一执行ResourceFactory.register(envChangeCache)
//key只会是system.property.、mimetype.property.、redkale.cachesource(.|[)、redkale.datasource(.|[)和其他非redkale.开头的配置项
void updateEnvironmentProperty(String key, Object value, Properties envChangeCache, Properties sourceChangeCache) {
if (key == null || value == null) return;
String val = replaceValue(value.toString()).trim();
if (key.startsWith("redkale.datasource.") || key.startsWith("redkale.datasource[")
|| key.startsWith("redkale.cachesource.") || key.startsWith("redkale.cachesource[")) {
if (!Objects.equals(val, sourceProperties.getProperty(key))) {
if (sourceChangeCache == null) {
sourceProperties.put(key, val);
} else {
sourceChangeCache.put(key, val);
}
}
} else if (key.startsWith("system.property.")) {
String propName = key.substring("system.property.".length());
if (envChangeCache != null || System.getProperty(propName) == null) { //命令行传参数优先级高
System.setProperty(propName, val);
}
} else if (key.startsWith("mimetype.property.")) {
MimeType.add(key.substring("mimetype.property.".length()), val);
} else if (key.startsWith("property.")) {
Object old = resourceFactory.find(key, String.class);
if (!Objects.equals(val, old)) {
envProperties.put(key, val);
if (envChangeCache == null) {
resourceFactory.register(key, val);
} else {
envChangeCache.put(key, val);
}
}
} else {
if (key.startsWith("redkale.")) {
throw new RuntimeException("property " + key + " cannot redkale. startsWith");
}
String newkey = "property." + key;
Object old = resourceFactory.find(newkey, String.class);
if (!Objects.equals(val, old)) {
envProperties.put(key, val);
if (envChangeCache == null) {
resourceFactory.register(newkey, val);
} else {
envChangeCache.put(newkey, val);
}
}
}
}
void updateEnvironmentProperties(List<ResourceEvent> events) {
if (events == null || events.isEmpty()) return;
synchronized (envProperties) {
Properties envRegisterProps = new Properties();
Set<String> envRemovedKeys = new HashSet<>();
Properties envChangedProps = new Properties();
void updateSourceProperties(Properties sourceChangeCache) {
if (sourceChangeCache == null || sourceChangeCache.isEmpty()) return;
synchronized (sourceProperties) {
Properties changedProps = new Properties();
for (Map.Entry<Object, Object> en : sourceChangeCache.entrySet()) {
String key = en.getKey().toString();
if (key.startsWith("redkale.datasource.") || key.startsWith("redkale.datasource[")
|| key.startsWith("redkale.cachesource.") || key.startsWith("redkale.cachesource[")) {
if (!Objects.equals(en.getValue(), sourceProperties.get(key))) {
changedProps.put(en.getKey(), en.getValue());
if (key.endsWith(".name")) { //不更改source.name属性
throw new RuntimeException("source properties contains illegal key: " + key);
Set<String> sourceRemovedKeys = new HashSet<>();
Properties sourceChangedProps = new Properties();
for (ResourceEvent<String> event : events) {
if (event.name().startsWith("redkale.datasource.") || event.name().startsWith("redkale.datasource[")
|| event.name().startsWith("redkale.cachesource.") || event.name().startsWith("redkale.cachesource[")) {
if (event.name().endsWith(".name")) {
logger.log(Level.WARNING, "skip illegal key " + event.name() + " in source config, key cannot endsWith '.name'");
} else {
if (!Objects.equals(event.newValue(), sourceProperties.getProperty(event.name()))) {
if (event.newValue() == null) {
if (sourceProperties.containsKey(event.name())) {
sourceRemovedKeys.add(event.name());
}
} else {
sourceChangedProps.put(event.name(), event.newValue());
}
}
}
} else if (event.name().startsWith("system.property.")) {
String propName = event.name().substring("system.property.".length());
if (event.newValue() == null) {
System.getProperties().remove(propName);
} else {
System.setProperty(propName, event.newValue());
}
} else if (event.name().startsWith("mimetype.property.")) {
String propName = event.name().substring("system.property.".length());
if (event.newValue() != null) {
MimeType.add(propName, event.newValue());
}
} else if (event.name().startsWith("property.")) {
if (!Objects.equals(event.newValue(), envProperties.getProperty(event.name()))) {
envRegisterProps.put(event.name(), event.newValue());
if (event.newValue() == null) {
if (envProperties.containsKey(event.name())) {
envRemovedKeys.add(event.name());
}
} else {
envChangedProps.put(event.name(), event.newValue());
}
}
} else if (event.name().startsWith("redkale.")) {
logger.log(Level.WARNING, "not support the env property key " + event.name() + " on change event");
} else {
throw new RuntimeException("source properties contains illegal key: " + key);
if (!Objects.equals(event.newValue(), envProperties.getProperty(event.name()))) {
envRegisterProps.put("property." + event.name(), event.newValue());
if (event.newValue() == null) {
if (envProperties.containsKey(event.name())) {
envRemovedKeys.add(event.name());
}
} else {
envChangedProps.put(event.name(), event.newValue());
}
}
}
}
if (changedProps.isEmpty()) return; //无内容改变
AnyValue newRedNode = AnyValue.loadFromProperties(changedProps).getAnyValue("redkale");
AnyValue newCacheNode = newRedNode.getAnyValue("cachesource");
if (newCacheNode != null) {
newCacheNode.forEach(null, (sourceName, newConf) -> {
//普通配置项的变更
if (!envRegisterProps.isEmpty()) {
envProperties.putAll(envChangedProps);
envRemovedKeys.forEach(k -> envProperties.remove(k));
resourceFactory.register(envRegisterProps, "", Environment.class);
}
//数据源配置项的变更
if (!sourceChangedProps.isEmpty() || !sourceRemovedKeys.isEmpty()) {
Set<String> cacheSourceNames = new LinkedHashSet<>();
Set<String> dataSourceNames = new LinkedHashSet<>();
List<String> keys = new ArrayList<>();
keys.addAll(sourceRemovedKeys);
keys.addAll((Set) sourceChangedProps.keySet());
for (final String key : keys) {
if (key.startsWith("redkale.cachesource[")) {
cacheSourceNames.add(key.substring("redkale.cachesource[".length(), key.indexOf(']')));
} else if (key.startsWith("redkale.cachesource.")) {
cacheSourceNames.add(key.substring("redkale.cachesource.".length(), key.indexOf('.', "redkale.cachesource.".length())));
} else if (key.startsWith("redkale.datasource[")) {
dataSourceNames.add(key.substring("redkale.datasource[".length(), key.indexOf(']')));
} else if (key.startsWith("redkale.datasource.")) {
dataSourceNames.add(key.substring("redkale.datasource.".length(), key.indexOf('.', "redkale.datasource.".length())));
}
}
//更新缓存
for (String sourceName : cacheSourceNames) {
CacheSource source = Utility.find(cacheSources, s -> Objects.equals(s.resourceName(), sourceName));
if (source == null) return; //多余的数据源
DefaultAnyValue old = (DefaultAnyValue) findSourceConfig(sourceName, "cachesource");
old.merge(newConf);
List<ResourceEvent> events = new ArrayList<>();
changedProps.forEach((k, v) -> {
final DefaultAnyValue old = (DefaultAnyValue) findSourceConfig(sourceName, "cachesource");
Properties newProps = new Properties();
sourceProperties.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.cachesource[" + sourceName + "].";
int pos = key.indexOf(prefix);
@@ -1797,21 +1896,52 @@ public final class Application {
prefix = "redkale.cachesource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) return;
events.add(ResourceEvent.create(key.substring(prefix.length()), v, sourceProperties.get(key)));
if (pos < 0) return; //不是同一name数据源配置项
newProps.put(k, v);
});
((AbstractCacheSource) source).onResourceChange(events.toArray(new ResourceEvent[events.size()]));
});
}
AnyValue newSourceNode = newRedNode.getAnyValue("datasource");
if (newSourceNode != null) {
newSourceNode.forEach(null, (sourceName, newConf) -> {
List<ResourceEvent> changeEvents = new ArrayList<>();
sourceChangedProps.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.cachesource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.cachesource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) return; //不是同一name数据源配置项
newProps.put(k, v);
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), v, sourceProperties.getProperty(key)));
});
sourceRemovedKeys.forEach(k -> {
final String key = k;
String prefix = "redkale.cachesource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.cachesource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) return;
newProps.remove(k); //不是同一name数据源配置项
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), null, sourceProperties.getProperty(key)));
});
if (!changeEvents.isEmpty()) {
DefaultAnyValue back = old.copy();
old.replace(AnyValue.loadFromProperties(newProps).getAnyValue("redkale").getAnyValue("cachesource").getAnyValue(sourceName));
try {
((AbstractCacheSource) source).onResourceChange(changeEvents.toArray(new ResourceEvent[changeEvents.size()]));
} catch (RuntimeException e) {
old.replace(back); //还原配置
throw e;
}
}
}
//更新数据库
for (String sourceName : dataSourceNames) {
DataSource source = Utility.find(dataSources, s -> Objects.equals(s.resourceName(), sourceName));
if (source == null) return; //多余的数据源
DefaultAnyValue old = (DefaultAnyValue) findSourceConfig(sourceName, "datasource");
old.merge(newConf);
List<ResourceEvent> events = new ArrayList<>();
changedProps.forEach((k, v) -> {
Properties newProps = new Properties();
sourceProperties.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.datasource[" + sourceName + "].";
int pos = key.indexOf(prefix);
@@ -1819,13 +1949,48 @@ public final class Application {
prefix = "redkale.datasource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) return;
events.add(ResourceEvent.create(key.substring(prefix.length()), v, sourceProperties.get(key)));
if (pos < 0) return; //不是同一name数据源配置项
newProps.put(k, v);
});
((AbstractDataSource) source).onResourceChange(events.toArray(new ResourceEvent[events.size()]));
});
List<ResourceEvent> changeEvents = new ArrayList<>();
sourceChangedProps.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.datasource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.datasource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) return; //不是同一name数据源配置项
newProps.put(k, v);
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), v, sourceProperties.getProperty(key)));
});
sourceRemovedKeys.forEach(k -> {
final String key = k;
String prefix = "redkale.datasource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.datasource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) return;
newProps.remove(k); //不是同一name数据源配置项
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), null, sourceProperties.getProperty(key)));
});
if (!changeEvents.isEmpty()) {
DefaultAnyValue back = old.copy();
old.replace(AnyValue.loadFromProperties(newProps).getAnyValue("redkale").getAnyValue("datasource").getAnyValue(sourceName));
try {
((AbstractDataSource) source).onResourceChange(changeEvents.toArray(new ResourceEvent[changeEvents.size()]));
} catch (RuntimeException e) {
old.replace(back); //还原配置
throw e;
}
}
}
sourceRemovedKeys.forEach(k -> sourceProperties.remove(k));
sourceProperties.putAll(sourceChangedProps);
}
sourceProperties.putAll(changedProps);
}
}
@@ -1946,7 +2111,7 @@ public final class Application {
}
if (this.propertiesAgent != null) {
long s = System.currentTimeMillis();
this.propertiesAgent.destroy(config.getAnyValue("resources").getAnyValue("properties"));
this.propertiesAgent.destroy(config.getAnyValue("properties"));
logger.info(this.propertiesAgent.getClass().getSimpleName() + " destroy in " + (System.currentTimeMillis() - s) + " ms");
}
if (this.clientAsyncGroup != null) {

View File

@@ -211,7 +211,6 @@ public abstract class NodeServer {
//---------------------------------------------------------------------------------------------
final ResourceFactory appResFactory = application.getResourceFactory();
final TransportFactory appSncpTranFactory = application.getSncpTransportFactory();
final AnyValue resources = application.config.getAnyValue("resources");
final String confURI = appResFactory.find(RESNAME_APP_CONF_DIR, String.class);
//------------------------------------- 注册 Resource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
@@ -222,7 +221,7 @@ public abstract class NodeServer {
Class type = field.getType();
if (type != AnyValue.class && type != AnyValue[].class) return;
Object resource = null;
final AnyValue properties = resources == null ? null : resources.getAnyValue("properties");
final AnyValue properties = application.getAppConfig().getAnyValue("properties");
if (properties != null && type == AnyValue.class) {
resource = properties.getAnyValue(res.name().substring("properties.".length()));
appResFactory.register(resourceName, AnyValue.class, resource);
@@ -780,4 +779,114 @@ public abstract class NodeServer {
public String getThreadName() {
return this.threadName;
}
static final AnyValue.MergeFunction appConfigmergeFunction = (path, key, val1, val2) -> {
if ("".equals(path)) {
if ("executor".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("transport".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("excludelibs".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("cluster".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("listener".equals(key)) {
if (Objects.equals(val1.getValue("value"), val2.getValue("value"))) {
return AnyValue.MergeFunction.SKIP;
} else {
return AnyValue.MergeFunction.NONE;
}
}
if ("mq".equals(key)) {
if (Objects.equals(val1.getValue("name"), val2.getValue("name"))) {
return AnyValue.MergeFunction.REPLACE;
} else {
return AnyValue.MergeFunction.NONE;
}
}
if ("group".equals(key)) {
if (Objects.equals(val1.getValue("name"), val2.getValue("name"))) {
return AnyValue.MergeFunction.REPLACE;
} else {
return AnyValue.MergeFunction.NONE;
}
}
if ("server".equals(key)) {
if (Objects.equals(val1.getValue("name", val1.getValue("protocol") + "_" + val1.getValue("port")),
val2.getValue("name", val2.getValue("protocol") + "_" + val2.getValue("port")))) {
return AnyValue.MergeFunction.REPLACE;
} else {
return AnyValue.MergeFunction.NONE;
}
}
}
if ("cachesource".equals(path)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("datasource".equals(path)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("properties".equals(path)) {
if ("property".equals(key)) {
if (Objects.equals(val1.getValue("name"), val2.getValue("name"))) {
return AnyValue.MergeFunction.REPLACE;
} else {
return AnyValue.MergeFunction.NONE;
}
}
}
if ("server".equals(path)) {
if ("ssl".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("render".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("resource-servlet".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
}
if ("server.request".equals(path)) {
if ("remoteaddr".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("rpc".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("locale".equals(key)) {
if (Objects.equals(val1.getValue("name"), val2.getValue("name"))) {
return AnyValue.MergeFunction.REPLACE;
} else {
return AnyValue.MergeFunction.NONE;
}
}
}
if ("server.response".equals(path)) {
if ("content-type".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("defcookie".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("options".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("date".equals(key)) {
return AnyValue.MergeFunction.REPLACE;
}
if ("addheader".equals(key) || "setheader".equals(key)) {
if (Objects.equals(val1.getValue("name"), val2.getValue("name"))) {
return AnyValue.MergeFunction.REPLACE;
} else {
return AnyValue.MergeFunction.NONE;
}
}
}
return AnyValue.MergeFunction.MERGE;
};
}

View File

@@ -2,14 +2,14 @@
*/
package org.redkale.boot;
import java.util.Properties;
import java.util.*;
import java.util.logging.Logger;
import org.redkale.util.*;
/**
* 配置源Agent, 在init方法内需要实现读取配置信息如果支持配置更改通知也需要在init里实现监听
*
* 配置项优先级: 本地配置 &#60; 配置中心 &#60; 环境变量
*
* 配置项优先级: 本地配置 &#60; 配置中心 &#60; 环境变量
*
*
* 详情见: https://redkale.org
@@ -44,8 +44,10 @@ public abstract class PropertiesAgent {
*
* @param application Application
* @param conf 节点配置
*
* @return 加载的配置项
*/
public abstract void init(Application application, AnyValue conf);
public abstract Properties init(Application application, AnyValue conf);
/**
* 销毁动作
@@ -54,21 +56,18 @@ public abstract class PropertiesAgent {
*/
public abstract void destroy(AnyValue conf);
protected void updateEnvironmentProperties(Application application, Properties props) {
if (props.isEmpty()) return;
Properties envChangeCache = new Properties();
Properties sourceChangeCache = new Properties();
props.forEach((k, v) -> application.updateEnvironmentProperty(k.toString(), v.toString().trim(), envChangeCache, sourceChangeCache));
if (!envChangeCache.isEmpty()) {
application.resourceFactory.register(envChangeCache, "", Environment.class);
}
if (!sourceChangeCache.isEmpty()) {
application.updateSourceProperties(sourceChangeCache);
}
}
protected void putEnvironmentProperty(Application application, String key, Object value) {
application.updateEnvironmentProperty(key, value, null, null);
protected void updateEnvironmentProperties(Application application, List<ResourceEvent> events) {
if (events == null || events.isEmpty()) return;
application.updateEnvironmentProperties(events);
// Properties envChangeCache = new Properties();
// Properties sourceChangeCache = new Properties();
// //props.forEach((k, v) -> application.updateEnvironmentProperty(k.toString(), v.toString().trim(), envChangeCache, sourceChangeCache));
// if (!envChangeCache.isEmpty()) {
// application.resourceFactory.register(envChangeCache, "", Environment.class);
// }
// if (!sourceChangeCache.isEmpty()) {
// application.updateSourceProperties(sourceChangeCache);
// }
}
protected void reconfigLogging(Application application, Properties loggingProperties) {

View File

@@ -75,7 +75,7 @@ public class TransportWatchService extends AbstractWatchService {
}
}
DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port);
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
for (AnyValue groupconf : application.getAppConfig().getAnyValues("group")) {
if (group.equals(groupconf.getValue("name"))) {
((DefaultAnyValue) groupconf).addValue("node", node);
break;
@@ -107,7 +107,7 @@ public class TransportWatchService extends AbstractWatchService {
}
}
}
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
for (AnyValue groupconf : application.getAppConfig().getAnyValues("group")) {
if (group.equals(groupconf.getValue("name"))) {
((DefaultAnyValue) groupconf).removeValue("node", DefaultAnyValue.create("addr", addr).addValue("port", port));
break;

View File

@@ -140,7 +140,8 @@ public abstract class DataSqlSource extends AbstractDataSource implements Functi
StringBuilder sb = new StringBuilder();
if (readConfProps == writeConfProps) {
List<ResourceEvent> allEvents = new ArrayList<>();
Properties newProps = new Properties(this.readConfProps);
Properties newProps = new Properties();
newProps.putAll(this.readConfProps);
for (ResourceEvent event : events) { //可能需要解密
String newValue = decryptProperty(event.name(), event.newValue().toString());
allEvents.add(ResourceEvent.create(event.name(), newValue, event.oldValue()));
@@ -154,8 +155,10 @@ public abstract class DataSqlSource extends AbstractDataSource implements Functi
} else {
List<ResourceEvent> readEvents = new ArrayList<>();
List<ResourceEvent> writeEvents = new ArrayList<>();
Properties newReadProps = new Properties(this.readConfProps);
Properties newWriteProps = new Properties(this.writeConfProps);
Properties newReadProps = new Properties();
newReadProps.putAll(this.readConfProps);
Properties newWriteProps = new Properties();
newWriteProps.putAll(this.writeConfProps);
for (ResourceEvent event : events) {
if (event.name().startsWith("read.")) {
String newName = event.name().substring("read.".length());

View File

@@ -33,11 +33,15 @@ public abstract class AnyValue {
*/
public static interface MergeFunction {
public static final int NONE = 0;
public static final int REPLACE = 1;
public static final int MERGE = 2;
public int apply(String name, AnyValue val1, AnyValue val2);
public static final int SKIP = 3;
public int apply(String path, String name, AnyValue val1, AnyValue val2);
}
/**
@@ -158,6 +162,8 @@ public abstract class AnyValue {
@Override
public DefaultAnyValue copy() {
DefaultAnyValue rs = new DefaultAnyValue(this.ignoreCase);
rs.predicate = this.predicate;
rs.parentArrayIndex = this.parentArrayIndex;
if (this.stringEntrys != null) {
rs.stringEntrys = new Entry[this.stringEntrys.length];
for (int i = 0; i < rs.stringEntrys.length; i++) {
@@ -178,15 +184,39 @@ public abstract class AnyValue {
}
/**
* 将另一个对象合并过来
* 将另一个对象替换本对象
*
* @param node0 代合并对象
* @param func 判断覆盖方式的函数
* @param node 替换的对象
*
* @return AnyValue
*/
@Override
public DefaultAnyValue merge(AnyValue node0, MergeFunction func) {
public DefaultAnyValue replace(AnyValue node) {
if (node != null) {
DefaultAnyValue rs = (DefaultAnyValue) node;
this.ignoreCase = rs.ignoreCase;
this.predicate = rs.predicate;
this.parentArrayIndex = rs.parentArrayIndex;
this.stringEntrys = rs.stringEntrys;
this.anyEntrys = rs.anyEntrys;
}
return this;
}
/**
* 将另一个对象合并过来
*
* @param node 代合并对象
* @param func 判断覆盖方式的函数
*
* @return AnyValue
*/
@Override
public DefaultAnyValue merge(AnyValue node, MergeFunction func) {
return merge(node, "", func);
}
protected DefaultAnyValue merge(AnyValue node0, String path, MergeFunction func) {
if (node0 == null) return this;
if (node0 == this) throw new IllegalArgumentException();
DefaultAnyValue node = (DefaultAnyValue) node0;
@@ -212,15 +242,19 @@ public abstract class AnyValue {
ok = true;
break;
} else {
int funcVal = func.apply(en.name, en.value, item.value);
int funcVal = func.apply(path, en.name, en.value, item.value);
if (funcVal == MergeFunction.MERGE) {
item.value.merge(en.value, func);
String subPath = path.isEmpty() ? en.name : (path + "." + en.name);
((DefaultAnyValue) item.value).merge(en.value, subPath, func);
ok = true;
break;
} else if (funcVal == MergeFunction.REPLACE) {
item.value = en.value.copy();
ok = true;
break;
} else if (funcVal == MergeFunction.SKIP) {
ok = true;
break;
}
}
}
@@ -489,12 +523,32 @@ public abstract class AnyValue {
return this;
}
public void clearParentArrayIndex(String name) {
for (Entry<AnyValue> item : getAnyValueEntrys(name)) {
if (item.value != null) {
((DefaultAnyValue) item.value).parentArrayIndex = -1;
}
}
}
public DefaultAnyValue removeAnyValues(String name) {
if (name == null || this.anyEntrys == null) return this;
this.anyEntrys = Utility.remove(this.anyEntrys, (t) -> name.equals(((Entry) t).name));
return this;
}
public DefaultAnyValue removeValue(String name, AnyValue value) {
if (name == null || value == null || this.anyEntrys == null) return this;
this.anyEntrys = Utility.remove(this.anyEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value));
return this;
}
public DefaultAnyValue removeStringValues(String name) {
if (name == null || this.stringEntrys == null) return this;
this.stringEntrys = Utility.remove(this.stringEntrys, (t) -> name.equals(((Entry) t).name));
return this;
}
public DefaultAnyValue removeValue(String name, String value) {
if (name == null || value == null || this.stringEntrys == null) return this;
this.stringEntrys = Utility.remove(this.stringEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value));
@@ -794,7 +848,7 @@ public abstract class AnyValue {
}
parent = child;
} else { //数组或Map结构, []中间是数字开头的视为数组其他视为map
String itemField = item.substring(0, pos); //[前面一部分
String itemField = item.substring(0, pos); //[前面一部分'sources[1]'中'sources'
String keyOrIndex = item.substring(pos + 1, item.indexOf(']'));
int realIndex = -1;
if (!keyOrIndex.isEmpty() && keyOrIndex.charAt(0) >= '0' && keyOrIndex.charAt(0) <= '9') {
@@ -808,7 +862,7 @@ public abstract class AnyValue {
for (int j = 0; j < i; j++) {
prefixKey += keys[j] + ".";
}
DefaultAnyValue array = prefixArray.get(prefixKey + item);
DefaultAnyValue array = prefixArray.get(prefixKey + item); //item: [1]
if (array == null) {
final int ii = i;
String findkey = prefixKey + itemField + "[";
@@ -1034,21 +1088,19 @@ public abstract class AnyValue {
public abstract AnyValue copy();
/**
* 将另一个对象合并过来
* 将另一个对象替换本对象
*
* @param node 代合并对象
* @param node 替换的对象
*
* @return AnyValue
*/
public AnyValue merge(AnyValue node) {
return merge(node, null);
}
public abstract AnyValue replace(AnyValue node);
/**
* 将另一个对象合并过来
*
* @param node 代合并对象
* @param func 判断覆盖方式的函数
* @param func 覆盖方式的函数
*
* @return AnyValue
*/

View File

@@ -2,6 +2,7 @@
*/
package org.redkale.util;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -37,6 +38,31 @@ public interface ResourceEvent<T> {
return false;
}
public static List<ResourceEvent> create(Properties oldProps, Properties newProps) {
List<ResourceEvent> rs = new ArrayList<>();
if (oldProps == null && newProps == null) {
return rs;
}
if (oldProps == null) {
newProps.forEach((k, v) -> rs.add(ResourceEvent.create(k.toString(), v, null)));
} else if (newProps == null) {
oldProps.forEach((k, v) -> rs.add(ResourceEvent.create(k.toString(), null, v)));
} else {
newProps.forEach((k, v) -> {
String oldVal = oldProps.getProperty(k.toString());
if (!Objects.equals(v, oldVal)) {
rs.add(ResourceEvent.create(k.toString(), v, oldVal));
}
});
oldProps.forEach((k, v) -> {
if (!newProps.containsKey(k)) {
rs.add(ResourceEvent.create(k.toString(), null, v));
}
});
}
return rs;
}
public static <V> ResourceEvent<V> create(String name, V newValue, V oldValue) {
return new ResourceChangeEvent<>(name, newValue, oldValue);
}

View File

@@ -434,6 +434,7 @@ public final class ResourceFactory {
/**
* 将多个以指定资源名的String对象注入到资源池中
* properties的key一般以"property."开头
*
* @param properties 资源键值对
* @param environmentName 额外的资源名
@@ -441,7 +442,7 @@ public final class ResourceFactory {
*
*/
public <A> void register(Properties properties, String environmentName, Class<A> environmentType) {
if (properties == null) return;
if (properties == null || properties.isEmpty()) return;
List<ResourceChangeWrapper> wrappers = new ArrayList<>();
List<ResourceEvent> environmentEventList = new ArrayList<>();
properties.forEach((k, v) -> {