2.7.0-SNAPSHOT
This commit is contained in:
51
src/main/java/META-INF/application-template.properties
Normal file
51
src/main/java/META-INF/application-template.properties
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
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
|
||||
|
||||
#\u3010transport\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.transport.bufferCapacity = 32k
|
||||
redkale.resources.transport.bufferPoolSize = 32
|
||||
|
||||
#\u3010excludelibs\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.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.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.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
|
||||
|
||||
#\u3010listener\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.listener.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.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
|
||||
redkale.server[0].ssl.build = org.redkale.net.SSLBuilder\u5b50\u7c7b
|
||||
|
||||
redkale.server[0].services[0].autoload = true
|
||||
@@ -63,29 +63,29 @@
|
||||
<!--
|
||||
【节点全局唯一】
|
||||
第三方服务发现管理接口
|
||||
value: 类名,必须是org.redkale.cluster.ClusterAgent的子类
|
||||
waits: 注销服务后是否需要等待检查周期时间后再进行Service销毁,默认值为:false
|
||||
当一个Service进行服务注销后,不能立刻销毁Service,因为健康检测是有间隔时间差的,
|
||||
需要等待一个健康检测周期时间,让其他进程都更新完服务列表。
|
||||
如果使用MQ,可以设置为false,如果对服务健壮性要求高,建议设置为true
|
||||
protocols: 服务发现可以处理的协议, 默认值为: SNCP, 多个协议用分号;隔开
|
||||
ports: 服务发现可以处理的端口, 多个端口用分号;隔开
|
||||
-->
|
||||
<!--
|
||||
<cluster value="org.redkalex.cluster.consul.ConsulClusterAgent" waits="false" protocols="SNCP" ports="7070;7071">
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
</cluster>
|
||||
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不能重复,命名规则: 字母、数字、下划线
|
||||
value: 实现类名,必须是org.redkale.mq.MessageAgent的子类
|
||||
type: 实现类名,必须是org.redkale.mq.MessageAgent的子类
|
||||
coder: MessageRecord的解析器类,必须是org.redkale.mq.MessageCoder<MessageRecord>的实现类,
|
||||
可对数据包进行加密解密,默认值:org.redkale.mq.MessageRecordCoder
|
||||
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
|
||||
-->
|
||||
<!--
|
||||
<mq name="" value="org.redkalex.mq.kafka.KafkaMessageAgent">
|
||||
<mq name="" type="org.redkalex.mq.kafka.KafkaMessageAgent">
|
||||
<servers value="127.0.0.1:9101"/>
|
||||
<consumer>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
@@ -94,7 +94,7 @@
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
</producer>
|
||||
</mq>
|
||||
-->
|
||||
|
||||
<!--
|
||||
一个组包含多个node, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
||||
一个group节点对应一个 Transport 对象。
|
||||
@@ -111,18 +111,7 @@
|
||||
-->
|
||||
<node addr="127.0.0.1" port="7070"/>
|
||||
</group>
|
||||
|
||||
<!--
|
||||
全局的数据源设置, 可以是CacheSource、DataSource, JDBC的DataSource通常通过persistence.xml配置,此处多用于CacheSource的配置
|
||||
name: 资源名,用于依赖注入。
|
||||
value: 类名,必须是CacheSource或DataSource的子类,且必须实现Service接口。如果是DataSource.class,系统自动映射成DataJdbcSource.class
|
||||
groups: 指定groups。
|
||||
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
|
||||
-->
|
||||
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
|
||||
<node addr="127.0.0.1" port="7070"/>
|
||||
</source>
|
||||
|
||||
|
||||
<!--
|
||||
Application启动的监听事件,可配置多个节点
|
||||
value: 类名,必须是ApplicationListener的子类
|
||||
@@ -134,6 +123,8 @@
|
||||
全局的参数配置, 可以通过@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的实现子类。
|
||||
agent: 实现类名,必须是org.redkale.boot.PropertiesAgent的子类
|
||||
load: 加载文件,多个用;隔开。
|
||||
默认置入的system.property.的有:
|
||||
System.setProperty("redkale.net.transport.poolmaxconns", "100");
|
||||
@@ -146,7 +137,7 @@
|
||||
<properties>节点下也可包含非<property>节点.
|
||||
非<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
|
||||
-->
|
||||
<properties load="config.properties">
|
||||
<properties load="config.properties" agent="">
|
||||
<property name="system.property.yyyy" value="YYYYYY"/>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
@@ -286,6 +277,7 @@
|
||||
-->
|
||||
<request>
|
||||
<remoteaddr value="request.headers.X-RemoteAddress"/>
|
||||
<rpc authenticator="org.redkale.net.http.HttpRpcAuthenticator的实现类"/>
|
||||
</request>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -15,11 +15,16 @@ com.sun.level = INFO
|
||||
java.util.logging.FileHandler.limit = 20M
|
||||
java.util.logging.FileHandler.count = 100
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%tY%tm/log-%tY%tm%td.log
|
||||
#java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%tY%tm/log-warnerr-%tY%tm%td.log
|
||||
#\u9700\u8981\u5c4f\u853d\u6d88\u606f\u5185\u5bb9\u7684\u6b63\u5219\u8868\u8fbe\u5f0f
|
||||
java.util.logging.FileHandler.denyreg =
|
||||
java.util.logging.FileHandler.append = true
|
||||
|
||||
#java.util.logging.ConsoleHandler.level = FINE
|
||||
|
||||
#\u5c06\u65e5\u5fd7\u5199\u8fdbSearchSource, \u5fc5\u987b\u6307\u5b9asource\u8d44\u6e90\u540d\uff0c\u5728source.properties\u4e2d\u5b9a\u4e49
|
||||
#java.util.logging.SearchHandler.source = platfsearch
|
||||
#\u6307\u5b9a\u5199\u8fdbSearchSource\u7684\u8868\u540d\uff0c\u9ed8\u8ba4\u503c\u4e3alog-record
|
||||
#java.util.logging.SearchHandler.tag = log-${APP_NAME}-%tY%tm%td
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 其配置算是标准的JPA配置文件的缩略版 -->
|
||||
<!--
|
||||
【已废弃】,建议使用 source.properties
|
||||
其配置算是标准的JPA配置文件的缩略版
|
||||
-->
|
||||
<persistence>
|
||||
<!-- 系统基本库 -->
|
||||
<persistence-unit name="demouser">
|
||||
@@ -38,7 +41,7 @@
|
||||
<!-- IM消息库 -->
|
||||
<persistence-unit name="demoim">
|
||||
<properties>
|
||||
<!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&autoReconnectForPools=true&characterEncoding=utf8 -->
|
||||
<!-- jdbc:mysql://127.0.0.1:3306/dbim?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8 -->
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="123456"/>
|
||||
|
||||
50
src/main/java/META-INF/source-template.properties
Normal file
50
src/main/java/META-INF/source-template.properties
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
# CacheSource @Resource(name="usersession")
|
||||
# type\u53ef\u4ee5\u4e0d\u7528\u8bbe\u7f6e\uff0c\u6846\u67b6\u4f1a\u6839\u636eurl\u5224\u65ad\u4f7f\u7528\u54ea\u4e2aCacheSource\u5b9e\u73b0\u7c7b
|
||||
redkale.cachesource[usersession].type = org.redkalex.cache.redis.RedisCacheSource
|
||||
# \u6700\u5927\u8fde\u63a5\u6570
|
||||
redkale.cachesource[usersession].maxconns = 16
|
||||
# \u8282\u70b9\u5730\u5740
|
||||
redkale.cachesource[usersession].node[0].url = redis://127.0.0.1:6363
|
||||
# \u8282\u70b9\u5bc6\u7801
|
||||
redkale.cachesource[usersession].node[0].password = 12345678
|
||||
# \u8282\u70b9db
|
||||
redkale.cachesource[usersession].node[0].db = 0
|
||||
|
||||
#\u7b80\u5316\u5199\u6cd5: \u53ef\u4ee5\u4e0d\u7528.node[0], \u5c06\u53c2\u6570\u90fd\u5408\u5e76\u5230url\u4e2d
|
||||
redkale.cachesource[usersession].url = redis://user:123456@127.0.0.1:6363?db=0
|
||||
|
||||
|
||||
# DataSource @Resource(name="platf")
|
||||
# type\u53ef\u4ee5\u4e0d\u7528\u8bbe\u7f6e\uff0c\u6846\u67b6\u4f1a\u6839\u636eurl\u5224\u65ad\u4f7f\u7528\u54ea\u4e2aDataSource\u5b9e\u73b0\u7c7b\uff0c\u9ed8\u8ba4\u503c: org.redkale.source.DataJdbcSource
|
||||
redkale.datasource[platf].type = org.redkale.source.DataJdbcSource
|
||||
# \u662f\u5426\u5f00\u542f\u7f13\u5b58(\u6807\u8bb0\u4e3a@Cacheable\u7684Entity\u7c7b)\uff0c\u503c\u76ee\u524d\u53ea\u652f\u6301\u4e24\u79cd\uff1a ALL: \u6240\u6709\u5f00\u542f\u7f13\u5b58\u3002 NONE: \u5173\u95ed\u6240\u6709\u7f13\u5b58\uff0c \u975eNONE\u5b57\u6837\u7edf\u4e00\u89c6\u4e3aALL
|
||||
redkale.datasource[platf].cachemode = ALL
|
||||
# \u662f\u5426\u81ea\u52a8\u5efa\u8868\u5f53\u8868\u4e0d\u5b58\u5728\u7684\u65f6\u5019\uff0c \u76ee\u524d\u53ea\u652f\u6301mysql\u3001postgres\uff0c \u9ed8\u8ba4\u4e3afalse
|
||||
redkale.datasource[platf].table-autoddl = false
|
||||
# \u7528\u6237
|
||||
redkale.datasource[platf].user = root
|
||||
# \u5bc6\u7801
|
||||
redkale.datasource[platf].password = 12345678
|
||||
# \u591a\u4e2aURL\u7528;\u9694\u5f00\uff0c\u5982\u5206\u5e03\u5f0fSearchSource\u9700\u8981\u914d\u591a\u4e2aURL
|
||||
redkale.datasource[platf].url = jdbc:mysql://127.0.0.1:3306/platf?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
# \u6700\u5927\u8fde\u63a5\u6570\uff0c\u9ed8\u8ba4\u503c\uff1aCPU\u6570
|
||||
redkale.datasource[platf].maxconns = 16
|
||||
# \u5305\u542b\u7684SQL\u6a21\u677f\uff0c\u76f8\u5f53\u4e8e\u53cd\u5411LIKE\uff0c\u4e0d\u540c\u7684JDBC\u9a71\u52a8\u7684SQL\u8bed\u53e5\u4e0d\u4e00\u6837\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].contain-sqltemplate = LOCATE(${keystr}, ${column}) > 0
|
||||
# \u5305\u542b\u7684SQL\u6a21\u677f\uff0c\u76f8\u5f53\u4e8e\u53cd\u5411LIKE\uff0c\u4e0d\u540c\u7684JDBC\u9a71\u52a8\u7684SQL\u8bed\u53e5\u4e0d\u4e00\u6837\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].notcontain-sqltemplate = LOCATE(${keystr}, ${column}) = 0
|
||||
# \u590d\u5236\u8868\u7ed3\u6784\u7684SQL\u6a21\u677f\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].tablenotexist-sqlstates = 42000;42S02
|
||||
# \u590d\u5236\u8868\u7ed3\u6784\u7684SQL\u6a21\u677f\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].tablecopy-sqltemplate = CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}
|
||||
|
||||
|
||||
# DataSource \u8bfb\u5199\u5206\u79bb
|
||||
redkale.datasource[platf].read.url = jdbc:mysql://127.0.0.1:3306/platf_r?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
redkale.datasource[platf].read.user = root
|
||||
redkale.datasource[platf].read.password = 12345678
|
||||
|
||||
redkale.datasource[platf].write.url = jdbc:mysql://127.0.0.1:3306/platf_w?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
redkale.datasource[platf].write.user = root
|
||||
redkale.datasource[platf].write.password = 12345678
|
||||
@@ -17,20 +17,20 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Resource {
|
||||
|
||||
/**
|
||||
* AuthenticationType
|
||||
*/
|
||||
@Deprecated
|
||||
public enum AuthenticationType {
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
CONTAINER,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
APPLICATION
|
||||
}
|
||||
// /**
|
||||
// * AuthenticationType
|
||||
// */
|
||||
// @Deprecated
|
||||
// public enum AuthenticationType {
|
||||
// /**
|
||||
// * @deprecated
|
||||
// */
|
||||
// CONTAINER,
|
||||
// /**
|
||||
// * @deprecated
|
||||
// */
|
||||
// APPLICATION
|
||||
// }
|
||||
|
||||
/**
|
||||
* 资源名称
|
||||
@@ -45,39 +45,39 @@ public @interface Resource {
|
||||
* @return Class
|
||||
*/
|
||||
public Class<?> type() default Object.class;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return AuthenticationType
|
||||
*/
|
||||
@Deprecated
|
||||
public AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean shareable() default true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Deprecated
|
||||
public String description() default "";
|
||||
|
||||
/**
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Deprecated
|
||||
public String mappedName() default "";
|
||||
|
||||
/**
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Deprecated
|
||||
public String lookup() default "";
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return AuthenticationType
|
||||
// */
|
||||
// @Deprecated
|
||||
// public AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return boolean
|
||||
// */
|
||||
// @Deprecated
|
||||
// public boolean shareable() default true;
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return String
|
||||
// */
|
||||
// @Deprecated
|
||||
// public String description() default "";
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return String
|
||||
// */
|
||||
// @Deprecated
|
||||
// public String mappedName() default "";
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return String
|
||||
// */
|
||||
// @Deprecated
|
||||
// public String lookup() default "";
|
||||
}
|
||||
|
||||
@@ -89,11 +89,11 @@ public @interface Column {
|
||||
|
||||
/**
|
||||
* for OpenAPI Specification 3
|
||||
*
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String example() default "";
|
||||
|
||||
String example() default "";
|
||||
|
||||
/**
|
||||
* (Optional) Whether the column is included in SQL INSERT
|
||||
* statements generated by the persistence provider.
|
||||
@@ -122,7 +122,12 @@ public @interface Column {
|
||||
/**
|
||||
* (Optional) The column length. (Applies only if a
|
||||
* string-valued column is used.)
|
||||
* if type==String and length == 65535 then sqltype is text
|
||||
* if type==String and length == 65535 then sqltype is TEXT <br>
|
||||
* if type==String and length <= 16777215 then sqltype is MEDIUMTEXT <br>
|
||||
* if type==String and length > 16777215 then sqltype is LONGTEXT <br>
|
||||
* if type==byte[] and length <= 65535 then sqltype is BLOB <br>
|
||||
* if type==byte[] and length <= 16777215 then sqltype is MEDIUMBLOB <br>
|
||||
* if type==byte[] and length > 16777215 then sqltype is LONGBLOB <br>
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,6 @@ module redkale {
|
||||
uses org.redkale.convert.ConvertProvider;
|
||||
uses org.redkale.source.CacheSourceProvider;
|
||||
uses org.redkale.source.DataSourceProvider;
|
||||
uses org.redkale.util.ResourceInjectLoader;
|
||||
uses org.redkale.util.ResourceAnnotationProvider;
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.lang.reflect.*;
|
||||
import java.math.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.logging.*;
|
||||
import javax.persistence.*;
|
||||
@@ -31,7 +32,7 @@ import org.redkale.util.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public final class ApiDocsService {
|
||||
public final class ApiDocCommand {
|
||||
|
||||
private static final java.lang.reflect.Type TYPE_RETRESULT_OBJECT = new TypeToken<RetResult<Object>>() {
|
||||
}.getType();
|
||||
@@ -47,13 +48,26 @@ public final class ApiDocsService {
|
||||
|
||||
private final Application app; //Application全局对象
|
||||
|
||||
public ApiDocsService(Application app) {
|
||||
public ApiDocCommand(Application app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public void run(String[] args) throws Exception {
|
||||
public String command(String cmd, String[] params) throws Exception {
|
||||
//是否跳过RPC接口
|
||||
final boolean skipRPC = Arrays.toString(args).toLowerCase().contains("skip-rpc") && !Arrays.toString(args).toLowerCase().contains("skip-rpc=false");
|
||||
boolean skipRPC = true;
|
||||
String apiHost = "http://localhost";
|
||||
|
||||
if (params != null && params.length > 0) {
|
||||
for (String param : params) {
|
||||
if (param == null) continue;
|
||||
param = param.toLowerCase();
|
||||
if (param.startsWith("--api-skiprpc=")) {
|
||||
skipRPC = "true".equalsIgnoreCase(param.substring("--api-skiprpc=".length()));
|
||||
} else if (param.startsWith("--api-host=")) {
|
||||
apiHost = param.substring("--api-host=".length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Map> serverList = new ArrayList<>();
|
||||
Field __prefix = HttpServlet.class.getDeclaredField("_prefix");
|
||||
@@ -70,7 +84,7 @@ public final class ApiDocsService {
|
||||
serverList.add(map);
|
||||
HttpServer server = node.getServer();
|
||||
map.put("address", server.getSocketAddress());
|
||||
swaggerServers.add(Utility.ofMap("url", "http://localhost:" + server.getSocketAddress().getPort()));
|
||||
swaggerServers.add(Utility.ofMap("url", apiHost + ":" + server.getSocketAddress().getPort()));
|
||||
List<Map<String, Object>> servletsList = new ArrayList<>();
|
||||
map.put("servlets", servletsList);
|
||||
String plainContentType = server.getResponseConfig() == null ? "application/json" : server.getResponseConfig().plainContentType;
|
||||
@@ -200,7 +214,7 @@ public final class ApiDocsService {
|
||||
f.setAccessible(true);
|
||||
paramGenericType = (Type) f.get(servlet);
|
||||
}
|
||||
simpleSchemaType(node.getLogger(), swaggerComponentsMap, param.type(), paramGenericType, paramSchemaMap, true);
|
||||
simpleSchemaType(null, node.getLogger(), swaggerComponentsMap, param.type(), paramGenericType, paramSchemaMap, true);
|
||||
if (param.style() == HttpParam.HttpParameterStyle.BODY) {
|
||||
swaggerRequestBody.put("description", param.comment());
|
||||
swaggerRequestBody.put("content", Utility.ofMap(plainContentType, Utility.ofMap("schema", paramSchemaMap)));
|
||||
@@ -217,9 +231,10 @@ public final class ApiDocsService {
|
||||
swaggerParamMap.put("style", param.style() == HttpParam.HttpParameterStyle.HEADER || param.name().indexOf('#') == 0 ? "simple" : "form");
|
||||
swaggerParamMap.put("explode", true);
|
||||
swaggerParamMap.put("schema", paramSchemaMap);
|
||||
Object example = formatExample(param.example(), param.type(), paramGenericType);
|
||||
if (example != null) swaggerParamMap.put("example", example);
|
||||
if (!param.example().isEmpty()) {
|
||||
Object example = formatExample(null, param.example(), param.type(), paramGenericType);
|
||||
if (example != null) {
|
||||
swaggerParamMap.put("example", example);
|
||||
} else if (!param.example().isEmpty()) {
|
||||
swaggerParamMap.put("example", param.example());
|
||||
}
|
||||
swaggerParamsList.add(swaggerParamMap);
|
||||
@@ -276,12 +291,13 @@ public final class ApiDocsService {
|
||||
swaggerOperatMap.put("deprecated", true);
|
||||
}
|
||||
Map<String, Object> respSchemaMap = new LinkedHashMap<>();
|
||||
simpleSchemaType(node.getLogger(), swaggerComponentsMap, action.result(), resultType, respSchemaMap, true);
|
||||
JsonFactory returnFactory = Rest.createJsonFactory(false, method.getAnnotationsByType(RestConvert.class), method.getAnnotationsByType(RestConvertCoder.class));
|
||||
simpleSchemaType(returnFactory, node.getLogger(), swaggerComponentsMap, action.result(), resultType, respSchemaMap, true);
|
||||
|
||||
Map<String, Object> respMap = new LinkedHashMap<>();
|
||||
respMap.put("schema", respSchemaMap);
|
||||
Object example = formatExample(action.example(), action.result(), resultType);
|
||||
if (example != null) swaggerOperatMap.put("example", example);
|
||||
Object example = formatExample(returnFactory, action.example(), action.result(), resultType);
|
||||
if (example != null) respSchemaMap.put("example", example);
|
||||
if (!swaggerRequestBody.isEmpty()) swaggerOperatMap.put("requestBody", swaggerRequestBody);
|
||||
swaggerOperatMap.put("parameters", swaggerParamsList);
|
||||
String actiondesc = action.comment();
|
||||
@@ -335,16 +351,18 @@ public final class ApiDocsService {
|
||||
if (doctemplate.isFile() && doctemplate.canRead()) {
|
||||
in = new FileInputStream(doctemplate);
|
||||
}
|
||||
if (in == null) in = ApiDocsService.class.getResourceAsStream("apidoc-template.html");
|
||||
String content = Utility.read(in).replace("'${content}'", json);
|
||||
in.close();
|
||||
FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html"));
|
||||
outhtml.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
outhtml.close();
|
||||
if (in != null) {
|
||||
String content = Utility.read(in).replace("'${content}'", json);
|
||||
in.close();
|
||||
FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html"));
|
||||
outhtml.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
outhtml.close();
|
||||
}
|
||||
}
|
||||
return "apidoc success";
|
||||
}
|
||||
|
||||
private static void simpleSchemaType(Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType, Map<String, Object> schemaMap, boolean recursive) {
|
||||
private static void simpleSchemaType(JsonFactory factory, Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType, Map<String, Object> schemaMap, boolean recursive) {
|
||||
if (type == int.class || type == Integer.class || type == AtomicInteger.class) {
|
||||
schemaMap.put("type", "integer");
|
||||
schemaMap.put("format", "int32");
|
||||
@@ -368,13 +386,13 @@ public final class ApiDocsService {
|
||||
schemaMap.put("type", "array");
|
||||
Map<String, Object> sbumap = new LinkedHashMap<>();
|
||||
if (type.isArray()) {
|
||||
simpleSchemaType(logger, componentsMap, type.getComponentType(), type.getComponentType(), sbumap, false);
|
||||
simpleSchemaType(factory, logger, componentsMap, type.getComponentType(), type.getComponentType(), sbumap, false);
|
||||
} else if (genericType instanceof ParameterizedType) {
|
||||
Type subpt = ((ParameterizedType) genericType).getActualTypeArguments()[0];
|
||||
if (subpt instanceof Class) {
|
||||
simpleSchemaType(logger, componentsMap, (Class) subpt, subpt, sbumap, false);
|
||||
simpleSchemaType(factory, logger, componentsMap, (Class) subpt, subpt, sbumap, false);
|
||||
} else if (subpt instanceof ParameterizedType && ((ParameterizedType) subpt).getOwnerType() instanceof Class) {
|
||||
simpleSchemaType(logger, componentsMap, (Class) ((ParameterizedType) subpt).getOwnerType(), subpt, sbumap, false);
|
||||
simpleSchemaType(factory, logger, componentsMap, (Class) ((ParameterizedType) subpt).getOwnerType(), subpt, sbumap, false);
|
||||
} else {
|
||||
sbumap.put("type", "object");
|
||||
}
|
||||
@@ -383,7 +401,7 @@ public final class ApiDocsService {
|
||||
}
|
||||
schemaMap.put("items", sbumap);
|
||||
} else if (!type.getName().startsWith("java.") && !type.getName().startsWith("javax.")) {
|
||||
String ct = simpleComponentType(logger, componentsMap, type, genericType);
|
||||
String ct = simpleComponentType(factory, logger, componentsMap, type, genericType);
|
||||
if (ct == null) {
|
||||
schemaMap.put("type", "object");
|
||||
} else {
|
||||
@@ -394,10 +412,11 @@ public final class ApiDocsService {
|
||||
}
|
||||
}
|
||||
|
||||
private static String simpleComponentType(Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType) {
|
||||
private static String simpleComponentType(JsonFactory factory, Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType) {
|
||||
try {
|
||||
Set<Type> types = new HashSet<>();
|
||||
Encodeable encodeable = JsonFactory.root().loadEncoder(genericType);
|
||||
String ct = componentKey(logger, componentsMap, null, encodeable, true);
|
||||
String ct = componentKey(factory, logger, types, componentsMap, null, encodeable, true);
|
||||
if (ct == null || ct.length() == 0) return null;
|
||||
if (componentsMap.containsKey(ct)) return ct;
|
||||
Map<String, Object> cmap = new LinkedHashMap<>();
|
||||
@@ -409,7 +428,7 @@ public final class ApiDocsService {
|
||||
if (encodeable instanceof ObjectEncoder) {
|
||||
for (EnMember member : ((ObjectEncoder) encodeable).getMembers()) {
|
||||
Map<String, Object> schemaMap = new LinkedHashMap<>();
|
||||
simpleSchemaType(logger, componentsMap, TypeToken.typeToClassOrElse(member.getEncoder().getType(), Object.class), member.getEncoder().getType(), schemaMap, true);
|
||||
simpleSchemaType(factory, logger, componentsMap, TypeToken.typeToClassOrElse(member.getEncoder().getType(), Object.class), member.getEncoder().getType(), schemaMap, true);
|
||||
String desc = "";
|
||||
if (member.getField() != null) {
|
||||
Column col = member.getField().getAnnotation(Column.class);
|
||||
@@ -455,35 +474,41 @@ public final class ApiDocsService {
|
||||
}
|
||||
}
|
||||
|
||||
private static String componentKey(Logger logger, Map<String, Map<String, Object>> componentsMap, EnMember field, Encodeable encodeable, boolean first) {
|
||||
private static String componentKey(JsonFactory factory, Logger logger, Set<Type> types, Map<String, Map<String, Object>> componentsMap, EnMember field, Encodeable encodeable, boolean first) {
|
||||
if (encodeable instanceof ObjectEncoder) {
|
||||
if (types.contains(encodeable.getType())) return "";
|
||||
types.add(encodeable.getType());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(((ObjectEncoder) encodeable).getTypeClass().getSimpleName());
|
||||
for (EnMember member : ((ObjectEncoder) encodeable).getMembers()) {
|
||||
if (member.getEncoder() instanceof ArrayEncoder
|
||||
|| member.getEncoder() instanceof CollectionEncoder) {
|
||||
String subsb = componentKey(logger, componentsMap, member, member.getEncoder(), false);
|
||||
String subsb = componentKey(factory, logger, types, componentsMap, member, member.getEncoder(), false);
|
||||
if (subsb == null) return null;
|
||||
AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField();
|
||||
if (real == null) continue;
|
||||
Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType();
|
||||
Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType();
|
||||
if (cz == ct) continue;
|
||||
if (field == null && encodeable.getType() instanceof Class) continue;
|
||||
if (sb.length() > 0 && subsb.length() > 0) sb.append("_");
|
||||
sb.append(subsb);
|
||||
} else if (member.getEncoder() instanceof ObjectEncoder || member.getEncoder() instanceof SimpledCoder) {
|
||||
AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField();
|
||||
if (real == null) continue;
|
||||
if (types.contains(member.getEncoder().getType())) continue;
|
||||
types.add(member.getEncoder().getType());
|
||||
if (member.getEncoder() instanceof SimpledCoder) {
|
||||
simpleSchemaType(logger, componentsMap, ((SimpledCoder) member.getEncoder()).getType(), ((SimpledCoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true);
|
||||
simpleSchemaType(factory, logger, componentsMap, ((SimpledCoder) member.getEncoder()).getType(), ((SimpledCoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true);
|
||||
} else {
|
||||
simpleSchemaType(logger, componentsMap, ((ObjectEncoder) member.getEncoder()).getTypeClass(), ((ObjectEncoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true);
|
||||
simpleSchemaType(factory, logger, componentsMap, ((ObjectEncoder) member.getEncoder()).getTypeClass(), ((ObjectEncoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true);
|
||||
}
|
||||
Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType();
|
||||
Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType();
|
||||
if (cz == ct) continue;
|
||||
String subsb = componentKey(logger, componentsMap, member, member.getEncoder(), false);
|
||||
String subsb = componentKey(factory, logger, types, componentsMap, member, member.getEncoder(), false);
|
||||
if (subsb == null) return null;
|
||||
if (field == null && member.getEncoder().getType() instanceof Class) continue;
|
||||
if (sb.length() > 0 && subsb.length() > 0) sb.append("_");
|
||||
sb.append(subsb);
|
||||
} else if (member.getEncoder() instanceof MapEncoder) {
|
||||
@@ -497,7 +522,7 @@ public final class ApiDocsService {
|
||||
final boolean array = (encodeable instanceof ArrayEncoder);
|
||||
Encodeable subEncodeable = array ? ((ArrayEncoder) encodeable).getComponentEncoder() : ((CollectionEncoder) encodeable).getComponentEncoder();
|
||||
if (subEncodeable instanceof SimpledCoder && field != null) return "";
|
||||
final String sb = componentKey(logger, componentsMap, null, subEncodeable, false);
|
||||
final String sb = componentKey(factory, logger, types, componentsMap, null, subEncodeable, false);
|
||||
if (sb == null || sb.isEmpty()) return sb;
|
||||
if (field != null && field.getField() != null && field.getField().getDeclaringClass() == Sheet.class) {
|
||||
return sb;
|
||||
@@ -516,8 +541,9 @@ public final class ApiDocsService {
|
||||
}
|
||||
}
|
||||
|
||||
private static Object formatExample(String example, Class type, Type genericType) {
|
||||
if (example == null || example.isEmpty()) return null;
|
||||
private static Object formatExample(JsonFactory factory, String example, Class type, Type genericType) {
|
||||
if (example != null && !example.isEmpty()) return example;
|
||||
JsonFactory jsonFactory = factory == null || factory == JsonFactory.root() ? exampleFactory : factory;
|
||||
if (type == Flipper.class) {
|
||||
return new Flipper();
|
||||
} else if (TYPE_RETRESULT_OBJECT.equals(genericType)) {
|
||||
@@ -528,8 +554,103 @@ public final class ApiDocsService {
|
||||
return RetResult.success(0);
|
||||
} else if (TYPE_RETRESULT_LONG.equals(genericType)) {
|
||||
return RetResult.success(0L);
|
||||
} else if (type == boolean.class || type == Boolean.class) {
|
||||
return true;
|
||||
} else if (type.isPrimitive()) {
|
||||
return 0;
|
||||
} else if (type == boolean[].class || type == Boolean[].class) {
|
||||
return new boolean[]{true, false};
|
||||
} else if (type == byte[].class || type == Byte[].class) {
|
||||
return new byte[]{0, 0};
|
||||
} else if (type == char[].class || type == Character[].class) {
|
||||
return new char[]{'a', 'b'};
|
||||
} else if (type == short[].class || type == Short[].class) {
|
||||
return new short[]{0, 0};
|
||||
} else if (type == int[].class || type == Integer[].class) {
|
||||
return new int[]{0, 0};
|
||||
} else if (type == long[].class || type == Long[].class) {
|
||||
return new long[]{0, 0};
|
||||
} else if (type == float[].class || type == Float[].class) {
|
||||
return new float[]{0, 0};
|
||||
} else if (type == double[].class || type == Double[].class) {
|
||||
return new double[]{0, 0};
|
||||
} else if (Number.class.isAssignableFrom(type)) {
|
||||
return 0;
|
||||
} else if (CharSequence.class.isAssignableFrom(type)) {
|
||||
return "";
|
||||
} else if (CompletableFuture.class.isAssignableFrom(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
return formatExample(factory, example, valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : ((Class) valType), valType);
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (Sheet.class.isAssignableFrom(type)) { //要在Collection前面
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
Class valClass = valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : (Class) valType;
|
||||
Object val = formatExample(factory, example, valClass, valType);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "{'rows':[" + val + "," + val + "]}")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (type.isArray()) {
|
||||
try {
|
||||
Object val = formatExample(factory, example, type.getComponentType(), type.getComponentType());
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "[" + val + "," + val + "]")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
Class valClass = valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : (Class) valType;
|
||||
Object val = formatExample(factory, example, valClass, valType);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "[" + val + "," + val + "]")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (type == RetResult.class) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
Class valClass = valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : (Class) valType;
|
||||
Object val = formatExample(factory, example, valClass, valType);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "{'result':" + val + "}")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (type != void.class) {
|
||||
try {
|
||||
Decodeable decoder = jsonFactory.loadDecoder(genericType);
|
||||
if (decoder instanceof ObjectDecoder) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
json.append("{");
|
||||
int index = 0;
|
||||
for (DeMember member : ((ObjectDecoder) decoder).getMembers()) {
|
||||
if (!(member.getDecoder() instanceof ObjectDecoder)) continue;
|
||||
if (index > 0) json.append(",");
|
||||
json.append('"').append(member.getAttribute().field()).append("\":{}");
|
||||
index++;
|
||||
}
|
||||
json.append("}");
|
||||
Object val = jsonFactory.getConvert().convertFrom(genericType, json.toString());
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(val));
|
||||
}
|
||||
Creator creator = Creator.create(type);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(creator.create()));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
return example;
|
||||
}
|
||||
|
||||
private static final JsonFactory exampleFactory = JsonFactory.create().tiny(false);
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ import java.util.function.Predicate;
|
||||
import java.util.jar.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.regex.*;
|
||||
import javax.annotation.Priority;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
|
||||
@@ -103,11 +104,12 @@ public final class ClassFilter<T> {
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getFilterEntrys() {
|
||||
HashSet<FilterEntry<T>> set = new HashSet<>();
|
||||
set.addAll(entrys);
|
||||
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterEntrys()));
|
||||
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterEntrys()));
|
||||
return set;
|
||||
List<FilterEntry<T>> list = new ArrayList<>();
|
||||
list.addAll(entrys);
|
||||
if (ors != null) ors.forEach(f -> list.addAll(f.getFilterEntrys()));
|
||||
if (ands != null) ands.forEach(f -> list.addAll(f.getFilterEntrys()));
|
||||
Collections.sort(list);
|
||||
return new LinkedHashSet<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,11 +118,12 @@ public final class ClassFilter<T> {
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getFilterExpectEntrys() {
|
||||
HashSet<FilterEntry<T>> set = new HashSet<>();
|
||||
set.addAll(expectEntrys);
|
||||
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
|
||||
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
|
||||
return set;
|
||||
List<FilterEntry<T>> list = new ArrayList<>();
|
||||
list.addAll(expectEntrys);
|
||||
if (ors != null) ors.forEach(f -> list.addAll(f.getFilterExpectEntrys()));
|
||||
if (ands != null) ands.forEach(f -> list.addAll(f.getFilterExpectEntrys()));
|
||||
Collections.sort(list);
|
||||
return new LinkedHashSet<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +132,7 @@ public final class ClassFilter<T> {
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getAllFilterEntrys() {
|
||||
HashSet<FilterEntry<T>> rs = new HashSet<>();
|
||||
HashSet<FilterEntry<T>> rs = new LinkedHashSet<>();
|
||||
rs.addAll(getFilterEntrys());
|
||||
rs.addAll(getFilterExpectEntrys());
|
||||
return rs;
|
||||
@@ -384,7 +387,7 @@ public final class ClassFilter<T> {
|
||||
*
|
||||
* @param <T> 泛型
|
||||
*/
|
||||
public static final class FilterEntry<T> {
|
||||
public static final class FilterEntry<T> implements Comparable<FilterEntry<T>> {
|
||||
|
||||
private final HashSet<String> groups = new LinkedHashSet<>();
|
||||
|
||||
@@ -416,6 +419,14 @@ public final class ClassFilter<T> {
|
||||
this.name = property == null ? "" : property.getValue("name", "");
|
||||
}
|
||||
|
||||
@Override //@Priority值越大,优先级越高, 需要排前面
|
||||
public int compareTo(FilterEntry o) {
|
||||
if (!(o instanceof FilterEntry)) return 1;
|
||||
Priority p1 = this.type.getAnnotation(Priority.class);
|
||||
Priority p2 = ((FilterEntry<T>) o).type.getAnnotation(Priority.class);
|
||||
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
|
||||
@@ -465,6 +476,7 @@ public final class ClassFilter<T> {
|
||||
public boolean isExpect() {
|
||||
return expect;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
20
src/main/java/org/redkale/boot/LoggingBaseHandler.java
Normal file
20
src/main/java/org/redkale/boot/LoggingBaseHandler.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
|
||||
/**
|
||||
* Handler基类
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public abstract class LoggingBaseHandler extends Handler {
|
||||
|
||||
protected Application currentApplication() {
|
||||
return Application.currentApplication; //不能直接暴露外界访问
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,13 @@ import org.redkale.util.RedkaleClassLoader;
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import static java.nio.file.StandardCopyOption.*;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.regex.Pattern;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 自定义的日志输出类
|
||||
@@ -26,11 +26,13 @@ import java.util.regex.Pattern;
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class LoggingFileHandler extends Handler {
|
||||
public class LoggingFileHandler extends LoggingBaseHandler {
|
||||
|
||||
//public static final String FORMATTER_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s%n%5$s%6$s%n";
|
||||
public static final String FORMATTER_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
|
||||
|
||||
static boolean traceflag = false; //防止设置system.property前调用Traces类导致enable提前初始化
|
||||
|
||||
/**
|
||||
* SNCP的日志输出Handler
|
||||
*/
|
||||
@@ -42,6 +44,46 @@ public class LoggingFileHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoggingConsoleHandler extends ConsoleHandler {
|
||||
|
||||
private Pattern denyreg;
|
||||
|
||||
public LoggingConsoleHandler() {
|
||||
super();
|
||||
configure();
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
String denyregstr = manager.getProperty("java.util.logging.ConsoleHandler.denyreg");
|
||||
try {
|
||||
if (denyregstr != null && !denyregstr.trim().isEmpty()) {
|
||||
denyreg = Pattern.compile(denyregstr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord log) {
|
||||
if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
|
||||
if (traceflag && Traces.enable()) {
|
||||
String traceid = Traces.currTraceid();
|
||||
if (traceid == null || traceid.isEmpty()) {
|
||||
traceid = "[TID:N/A] ";
|
||||
} else {
|
||||
traceid = "[TID:" + traceid + "] ";
|
||||
}
|
||||
if (log.getMessage() == null) {
|
||||
log.setMessage(traceid);
|
||||
} else {
|
||||
log.setMessage(traceid + log.getMessage());
|
||||
}
|
||||
}
|
||||
super.publish(log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的日志时间格式化类
|
||||
* 与SimpleFormatter的区别在于level不使用本地化
|
||||
@@ -51,6 +93,9 @@ public class LoggingFileHandler extends Handler {
|
||||
|
||||
@Override
|
||||
public String format(LogRecord log) {
|
||||
if (log.getThrown() == null && log.getMessage() != null && log.getMessage().startsWith("------")) {
|
||||
return formatMessage(log) + "\r\n";
|
||||
}
|
||||
String source;
|
||||
if (log.getSourceClassName() != null) {
|
||||
source = log.getSourceClassName();
|
||||
@@ -104,9 +149,13 @@ public class LoggingFileHandler extends Handler {
|
||||
|
||||
protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue();
|
||||
|
||||
private String pattern;
|
||||
protected String pattern;
|
||||
|
||||
private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
|
||||
protected String patternDateFormat; //需要时间格式化
|
||||
|
||||
protected String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
|
||||
|
||||
protected String unusualDateFormat; //需要时间格式化
|
||||
|
||||
private int limit; //文件大小限制
|
||||
|
||||
@@ -118,9 +167,9 @@ public class LoggingFileHandler extends Handler {
|
||||
|
||||
private long tomorrow;
|
||||
|
||||
private boolean append;
|
||||
protected boolean append;
|
||||
|
||||
private Pattern denyreg;
|
||||
protected Pattern denyreg;
|
||||
|
||||
private final AtomicLong loglength = new AtomicLong();
|
||||
|
||||
@@ -198,16 +247,14 @@ public class LoggingFileHandler extends Handler {
|
||||
}
|
||||
if (logstream == null) {
|
||||
logindex.incrementAndGet();
|
||||
java.time.LocalDate date = LocalDate.now();
|
||||
logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||
logfile = new File(patternDateFormat == null ? pattern : Utility.formatTime(patternDateFormat, -1, System.currentTimeMillis()));
|
||||
logfile.getParentFile().mkdirs();
|
||||
loglength.set(logfile.length());
|
||||
logstream = new FileOutputStream(logfile, append);
|
||||
}
|
||||
if (unusual != null && logunusualstream == null) {
|
||||
logunusualindex.incrementAndGet();
|
||||
java.time.LocalDate date = LocalDate.now();
|
||||
logunusualfile = new File(unusual.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||
logunusualfile = new File(unusualDateFormat == null ? unusual : Utility.formatTime(unusualDateFormat, -1, System.currentTimeMillis()));
|
||||
logunusualfile.getParentFile().mkdirs();
|
||||
logunusuallength.set(logunusualfile.length());
|
||||
logunusualstream = new FileOutputStream(logunusualfile, append);
|
||||
@@ -241,7 +288,7 @@ public class LoggingFileHandler extends Handler {
|
||||
String cname = LoggingFileHandler.class.getName();
|
||||
this.pattern = manager.getProperty(cname + ".pattern");
|
||||
if (this.pattern == null) {
|
||||
this.pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
||||
this.pattern = "logs-%tm/" + getPrefix() + "log-%td.log";
|
||||
} else {
|
||||
int pos = this.pattern.lastIndexOf('/');
|
||||
if (pos > 0) {
|
||||
@@ -250,6 +297,10 @@ public class LoggingFileHandler extends Handler {
|
||||
this.pattern = getPrefix() + this.pattern;
|
||||
}
|
||||
}
|
||||
if (this.pattern != null && this.pattern.contains("%")) { //需要时间格式化
|
||||
this.patternDateFormat = this.pattern;
|
||||
Utility.formatTime(this.patternDateFormat, -1, System.currentTimeMillis()); //测试时间格式是否正确
|
||||
}
|
||||
String unusualstr = manager.getProperty(cname + ".unusual");
|
||||
if (unusualstr != null) {
|
||||
int pos = unusualstr.lastIndexOf('/');
|
||||
@@ -259,6 +310,10 @@ public class LoggingFileHandler extends Handler {
|
||||
this.unusual = getPrefix() + unusualstr;
|
||||
}
|
||||
}
|
||||
if (this.unusual != null && this.unusual.contains("%")) { //需要时间格式化
|
||||
this.unusualDateFormat = this.unusual;
|
||||
Utility.formatTime(this.unusualDateFormat, -1, System.currentTimeMillis()); //测试时间格式是否正确
|
||||
}
|
||||
String limitstr = manager.getProperty(cname + ".limit");
|
||||
try {
|
||||
if (limitstr != null) {
|
||||
@@ -333,6 +388,7 @@ public class LoggingFileHandler extends Handler {
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord log) {
|
||||
if (!isLoggable(log)) return;
|
||||
final String sourceClassName = log.getSourceClassName();
|
||||
if (sourceClassName == null || true) {
|
||||
StackTraceElement[] ses = new Throwable().getStackTrace();
|
||||
@@ -346,6 +402,19 @@ public class LoggingFileHandler extends Handler {
|
||||
log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
|
||||
}
|
||||
if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
|
||||
if (traceflag && Traces.enable()) {
|
||||
String traceid = Traces.currTraceid();
|
||||
if (traceid == null || traceid.isEmpty()) {
|
||||
traceid = "[TID:N/A] ";
|
||||
} else {
|
||||
traceid = "[TID:" + traceid + "] ";
|
||||
}
|
||||
if (log.getMessage() == null) {
|
||||
log.setMessage(traceid);
|
||||
} else {
|
||||
log.setMessage(traceid + log.getMessage());
|
||||
}
|
||||
}
|
||||
logqueue.offer(log);
|
||||
}
|
||||
|
||||
|
||||
303
src/main/java/org/redkale/boot/LoggingSearchHandler.java
Normal file
303
src/main/java/org/redkale/boot/LoggingSearchHandler.java
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.*;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.persistence.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 基于SearchSource的日志输出类
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class LoggingSearchHandler extends LoggingBaseHandler {
|
||||
|
||||
protected static final String DEFAULT_TABLE_NAME = "log-record";
|
||||
|
||||
protected final LinkedBlockingQueue<SearchLogRecord> logqueue = new LinkedBlockingQueue();
|
||||
|
||||
protected final AtomicInteger retryCount = new AtomicInteger(3);
|
||||
|
||||
protected String tag = DEFAULT_TABLE_NAME; //用于表前缀, 默认是
|
||||
|
||||
protected String tagDateFormat; //需要时间格式化
|
||||
|
||||
protected String pattern;
|
||||
|
||||
protected Pattern denyreg;
|
||||
|
||||
protected String sourceResourceName;
|
||||
|
||||
protected SearchSource source;
|
||||
|
||||
public LoggingSearchHandler() {
|
||||
configure();
|
||||
open();
|
||||
}
|
||||
|
||||
private void open() {
|
||||
final String name = "Redkale-" + getClass().getSimpleName() + "-Thread";
|
||||
final int batchSize = 100; //批量最多100条
|
||||
final List<SearchLogRecord> logList = new ArrayList<>();
|
||||
final SimpleFormatter formatter = new SimpleFormatter();
|
||||
final PrintStream outStream = System.out;
|
||||
new Thread() {
|
||||
{
|
||||
setName(name);
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
SearchLogRecord log = logqueue.take();
|
||||
while (source == null && retryCount.get() > 0) initSource();
|
||||
//----------------------写日志-------------------------
|
||||
if (source == null) { //source加载失败
|
||||
outStream.print(formatter.format(log.rawLog));
|
||||
} else {
|
||||
logList.add(log);
|
||||
int size = batchSize;
|
||||
while (--size > 0) {
|
||||
log = logqueue.poll();
|
||||
if (log == null) break;
|
||||
logList.add(log);
|
||||
}
|
||||
source.insert(logList);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
|
||||
} finally {
|
||||
logList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private synchronized void initSource() {
|
||||
if (retryCount.get() < 1) return;
|
||||
try {
|
||||
Utility.sleep(3000); //如果SearchSource自身在打印日志,需要停顿一点时间让SearchSource初始化完成
|
||||
Application application = currentApplication();
|
||||
this.source = (SearchSource) application.loadDataSource(sourceResourceName, false);
|
||||
if (retryCount.get() == 1 && this.source == null) System.err.println("ERROR: not load logging.source(" + sourceResourceName + ")");
|
||||
} catch (Exception t) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, t, ErrorManager.WRITE_FAILURE);
|
||||
} finally {
|
||||
retryCount.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkTagName(String name) { //只能是字母、数字、短横、点、%、$和下划线
|
||||
if (name.isEmpty()) return false;
|
||||
for (char ch : name.toCharArray()) {
|
||||
if (ch >= '0' && ch <= '9') continue;
|
||||
if (ch >= 'a' && ch <= 'z') continue;
|
||||
if (ch >= 'A' && ch <= 'Z') continue;
|
||||
if (ch == '_' || ch == '-' || ch == '%' || ch == '$' || ch == '.') continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
String cname = getClass().getName();
|
||||
this.sourceResourceName = manager.getProperty(cname + ".source");
|
||||
if (this.sourceResourceName == null || this.sourceResourceName.isEmpty()) {
|
||||
throw new RuntimeException("not found logging.property " + cname + ".source");
|
||||
}
|
||||
String tagstr = manager.getProperty(cname + ".tag");
|
||||
if (tagstr != null && !tagstr.isEmpty()) {
|
||||
if (!checkTagName(tagstr.replaceAll("\\$\\{.+\\}", ""))) throw new RuntimeException("found illegal logging.property " + cname + ".tag = " + tagstr);
|
||||
this.tag = tagstr;
|
||||
if (tagstr.contains("%")) {
|
||||
this.tagDateFormat = this.tag;
|
||||
Utility.formatTime(this.tagDateFormat, -1, System.currentTimeMillis()); //测试时间格式是否正确
|
||||
}
|
||||
}
|
||||
|
||||
String levelstr = manager.getProperty(cname + ".level");
|
||||
try {
|
||||
if (levelstr != null) {
|
||||
Level l = Level.parse(levelstr);
|
||||
setLevel(l != null ? l : Level.ALL);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String filterstr = manager.getProperty(cname + ".filter");
|
||||
try {
|
||||
if (filterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
|
||||
setFilter((Filter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String formatterstr = manager.getProperty(cname + ".formatter");
|
||||
try {
|
||||
if (formatterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
|
||||
setFormatter((Formatter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
if (getFormatter() == null) setFormatter(new SimpleFormatter());
|
||||
|
||||
String encodingstr = manager.getProperty(cname + ".encoding");
|
||||
try {
|
||||
if (encodingstr != null) setEncoding(encodingstr);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
String denyregstr = manager.getProperty(cname + ".denyreg");
|
||||
try {
|
||||
if (denyregstr != null && !denyregstr.trim().isEmpty()) {
|
||||
denyreg = Pattern.compile(denyregstr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord log) {
|
||||
if (!isLoggable(log)) return;
|
||||
final String sourceClassName = log.getSourceClassName();
|
||||
if (sourceClassName == null || true) {
|
||||
StackTraceElement[] ses = new Throwable().getStackTrace();
|
||||
for (int i = 2; i < ses.length; i++) {
|
||||
if (ses[i].getClassName().startsWith("java.util.logging")) continue;
|
||||
log.setSourceClassName(ses[i].getClassName());
|
||||
log.setSourceMethodName(ses[i].getMethodName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
|
||||
String rawTag = tagDateFormat == null ? tag : Utility.formatTime(tagDateFormat, -1, log.getInstant().toEpochMilli());
|
||||
logqueue.offer(new SearchLogRecord(rawTag, log));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = DEFAULT_TABLE_NAME)
|
||||
@DistributeTable(strategy = SearchLogRecord.TableStrategy.class)
|
||||
public static class SearchLogRecord {
|
||||
|
||||
@Id
|
||||
@ConvertColumn(index = 1)
|
||||
@SearchColumn(options = "false")
|
||||
public String logid;
|
||||
|
||||
@ConvertColumn(index = 2)
|
||||
@SearchColumn(options = "false")
|
||||
public String level;
|
||||
|
||||
@ConvertColumn(index = 3)
|
||||
@SearchColumn(date = true)
|
||||
public long timestamp;
|
||||
|
||||
@ConvertColumn(index = 4)
|
||||
@SearchColumn(options = "false")
|
||||
public String traceid;
|
||||
|
||||
@ConvertColumn(index = 5)
|
||||
public String threadName;
|
||||
|
||||
@ConvertColumn(index = 6)
|
||||
@SearchColumn(text = true, options = "offsets")
|
||||
public String loggerName;
|
||||
|
||||
@ConvertColumn(index = 7)
|
||||
@SearchColumn(text = true, options = "offsets")
|
||||
public String methodName;
|
||||
|
||||
@ConvertColumn(index = 8)
|
||||
@SearchColumn(text = true, options = "offsets") //, analyzer = "ik_max_word"
|
||||
public String message; //log.message +"\r\n"+ log.thrown
|
||||
|
||||
@Transient
|
||||
@ConvertDisabled
|
||||
LogRecord rawLog;
|
||||
|
||||
@Transient
|
||||
@ConvertDisabled
|
||||
String rawTag;
|
||||
|
||||
public SearchLogRecord() {
|
||||
}
|
||||
|
||||
protected SearchLogRecord(String tag, LogRecord log) {
|
||||
this.rawLog = log;
|
||||
this.rawTag = tag;
|
||||
this.threadName = Thread.currentThread().getName();
|
||||
this.traceid = LoggingFileHandler.traceflag ? Traces.currTraceid() : null;
|
||||
String msg = log.getMessage();
|
||||
if (log.getThrown() != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
pw.println();
|
||||
log.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
String throwable = sw.toString();
|
||||
this.message = (msg != null && !msg.isEmpty()) ? (msg + "\r\n" + throwable) : throwable;
|
||||
} else {
|
||||
this.message = msg;
|
||||
}
|
||||
this.level = log.getLevel().toString();
|
||||
this.loggerName = log.getLoggerName();
|
||||
this.methodName = log.getSourceClassName() + " " + log.getSourceMethodName();
|
||||
this.timestamp = log.getInstant().toEpochMilli();
|
||||
this.logid = Utility.format36time(timestamp) + "_" + Utility.uuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
|
||||
public static class TableStrategy implements DistributeTableStrategy<SearchLogRecord> {
|
||||
|
||||
@Override
|
||||
public String getTable(String table, SearchLogRecord bean) {
|
||||
return bean.rawTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTable(String table, Serializable primary) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTable(String table, FilterNode node) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
|
||||
@Override
|
||||
protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
|
||||
if (httpServer != null) loadHttpFilter(filterFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,19 +102,19 @@ public class NodeHttpServer extends NodeServer {
|
||||
private void initWebSocketService() {
|
||||
final NodeServer self = this;
|
||||
final ResourceFactory regFactory = application.getResourceFactory();
|
||||
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
|
||||
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if (!(src instanceof WebSocketServlet)) return;
|
||||
ResourceFactory.ResourceLoader loader = null;
|
||||
if (!(srcObj instanceof WebSocketServlet)) return;
|
||||
ResourceTypeLoader loader = null;
|
||||
ResourceFactory sncpResFactory = null;
|
||||
for (NodeServer ns : application.servers) {
|
||||
if (!ns.isSNCP()) continue;
|
||||
sncpResFactory = ns.resourceFactory;
|
||||
loader = sncpResFactory.findLoader(WebSocketNode.class, field);
|
||||
loader = sncpResFactory.findTypeLoader(WebSocketNode.class, field);
|
||||
if (loader != null) break;
|
||||
}
|
||||
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
|
||||
if (loader != null) loader.load(sncpResFactory, srcResourceName, srcObj, resourceName, field, attachment);
|
||||
synchronized (regFactory) {
|
||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
|
||||
@@ -127,15 +127,15 @@ public class NodeHttpServer extends NodeServer {
|
||||
try {
|
||||
Field c = WebSocketServlet.class.getDeclaredField("messageAgent");
|
||||
c.setAccessible(true);
|
||||
messageAgent = (MessageAgent) c.get(src);
|
||||
messageAgent = (MessageAgent) c.get(srcObj);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex);
|
||||
}
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, org.redkale.net.http.WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
|
||||
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
||||
}
|
||||
resourceFactory.inject(nodeService, self);
|
||||
field.set(src, nodeService);
|
||||
resourceFactory.inject(resourceName, nodeService, self);
|
||||
field.set(srcObj, nodeService);
|
||||
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -145,7 +145,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
|
||||
protected void loadHttpFilter(final ClassFilter<? extends Filter> classFilter) throws Exception {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
@@ -212,66 +211,13 @@ 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, String.class);
|
||||
final Map<String, SimpleEntry<Class, AnyValue>> cacheResource = new HashMap<>();
|
||||
final Map<String, SimpleEntry<Class, AnyValue>> dataResources = new HashMap<>();
|
||||
if (resources != null) {
|
||||
for (AnyValue sourceConf : resources.getAnyValues("source")) {
|
||||
try {
|
||||
String classval = sourceConf.getValue("value");
|
||||
Class type = null;
|
||||
if (classval == null || classval.isEmpty()) {
|
||||
RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class);
|
||||
List<CacheSourceProvider> providers = new ArrayList<>();
|
||||
Iterator<CacheSourceProvider> it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator();
|
||||
while (it.hasNext()) {
|
||||
CacheSourceProvider s = it.next();
|
||||
if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName());
|
||||
if (s != null && s.acceptsConf(sourceConf)) {
|
||||
providers.add(s);
|
||||
}
|
||||
}
|
||||
Collections.sort(providers, (a, b) -> {
|
||||
Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class);
|
||||
Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class);
|
||||
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
});
|
||||
for (CacheSourceProvider provider : providers) {
|
||||
type = provider.sourceClass();
|
||||
if (type != null) break;
|
||||
}
|
||||
} else {
|
||||
type = serverClassLoader.loadClass(classval);
|
||||
}
|
||||
if (type == DataSource.class) {
|
||||
type = DataMemorySource.class;
|
||||
for (AnyValue itemConf : sourceConf.getAnyValues("property")) {
|
||||
if (itemConf.getValue("name", "").contains(DataSources.JDBC_URL)) {
|
||||
type = DataJdbcSource.class;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Service.class.isAssignableFrom(type)) {
|
||||
logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf);
|
||||
} else if (CacheSource.class.isAssignableFrom(type)) {
|
||||
cacheResource.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf));
|
||||
} else if (DataSource.class.isAssignableFrom(type)) {
|
||||
dataResources.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf));
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
final String confURI = appResFactory.find(RESNAME_APP_CONF_DIR, String.class);
|
||||
//------------------------------------- 注册 Resource --------------------------------------------------------
|
||||
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
||||
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
|
||||
try {
|
||||
Resource res = field.getAnnotation(Resource.class);
|
||||
if (res == null || !res.name().startsWith("properties.")) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
||||
if ((srcObj instanceof Service) && Sncp.isRemote((Service) srcObj)) return; //远程模式不得注入 DataSource
|
||||
Class type = field.getType();
|
||||
if (type != AnyValue.class && type != AnyValue[].class) return;
|
||||
Object resource = null;
|
||||
@@ -283,18 +229,18 @@ public abstract class NodeServer {
|
||||
resource = properties.getAnyValues(res.name().substring("properties.".length()));
|
||||
appResFactory.register(resourceName, AnyValue[].class, resource);
|
||||
}
|
||||
field.set(src, resource);
|
||||
field.set(srcObj, resource);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Resource inject error", e);
|
||||
}
|
||||
}, AnyValue.class, AnyValue[].class);
|
||||
|
||||
//------------------------------------- 注册 Local AutoLoad(false) Service --------------------------------------------------------
|
||||
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
||||
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
|
||||
Class<Service> resServiceType = Service.class;
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 AutoLoad Service
|
||||
if ((srcObj instanceof Service) && Sncp.isRemote((Service) srcObj)) return; //远程模式不得注入 AutoLoad Service
|
||||
if (!Service.class.isAssignableFrom(field.getType())) return;
|
||||
resServiceType = (Class) field.getType();
|
||||
if (resServiceType.getAnnotation(Local.class) == null) return;
|
||||
@@ -302,150 +248,56 @@ public abstract class NodeServer {
|
||||
if (al == null || al.value()) return;
|
||||
|
||||
//ResourceFactory resfactory = (isSNCP() ? appResFactory : resourceFactory);
|
||||
SncpClient client = src instanceof Service ? Sncp.getSncpClient((Service) src) : null;
|
||||
SncpClient client = srcObj instanceof Service ? Sncp.getSncpClient((Service) srcObj) : null;
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
final Set<String> groups = new HashSet<>();
|
||||
Service service = Modifier.isFinal(resServiceType.getModifiers()) ? (Service) resServiceType.getConstructor().newInstance() : Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null);
|
||||
appResFactory.register(resourceName, resServiceType, service);
|
||||
|
||||
field.set(src, service);
|
||||
rf.inject(service, self); // 给其可能包含@Resource的字段赋值;
|
||||
field.set(srcObj, service);
|
||||
rf.inject(resourceName, service, self); // 给其可能包含@Resource的字段赋值;
|
||||
if (!application.isCompileMode()) service.init(null);
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad service = " + resServiceType.getSimpleName() + ", resourceName = '" + resourceName + "')");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + src + " error", e);
|
||||
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + srcObj + " error", e);
|
||||
}
|
||||
}, Service.class);
|
||||
|
||||
//------------------------------------- 注册 DataSource --------------------------------------------------------
|
||||
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
||||
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
||||
SimpleEntry<Class, AnyValue> resEntry = dataResources.get(resourceName);
|
||||
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
|
||||
DataSource source = null;
|
||||
if (sourceConf != null) {
|
||||
final Class sourceType = resEntry.getKey();
|
||||
if (sourceType == DataJdbcSource.class) {
|
||||
source = DataSources.createDataSource(resourceName, sourceConf);
|
||||
} else {
|
||||
boolean can = false;
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
|
||||
for (Constructor cr : sourceType.getConstructors()) {
|
||||
if (cr.getParameterCount() == 0) {
|
||||
can = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数
|
||||
if (Modifier.isFinal(sourceType.getModifiers()) || sourceType.getAnnotation(Local.class) != null) {
|
||||
source = (DataSource) sourceType.getConstructor().newInstance();
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
|
||||
} else {
|
||||
final Service srcService = (Service) src;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
final Set<String> groups = new HashSet<>();
|
||||
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (source == null) {
|
||||
source = DataSources.createDataSource(confURI, resourceName); //从persistence.xml配置中创建
|
||||
}
|
||||
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(source.getClass(), source.getClass().getName());
|
||||
application.dataSources.add(source);
|
||||
ResourceType rt = source.getClass().getAnnotation(ResourceType.class);
|
||||
if (rt != null && rt.value() != DataSource.class) {
|
||||
appResFactory.register(resourceName, rt.value(), source);
|
||||
} else if (source instanceof SearchSource) {
|
||||
appResFactory.register(resourceName, SearchSource.class, source);
|
||||
}
|
||||
appResFactory.register(resourceName, DataSource.class, source);
|
||||
|
||||
field.set(src, source);
|
||||
rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值;
|
||||
//NodeServer.this.watchFactory.inject(src);
|
||||
if (!application.isCompileMode() && source instanceof Service) ((Service) source).init(sourceConf);
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load DataSource (type = " + source.getClass().getSimpleName() + ", resourceName = '" + resourceName + "')");
|
||||
if ((srcObj instanceof Service) && Sncp.isRemote((Service) srcObj)) return; //远程模式不得注入 DataSource
|
||||
DataSource source = application.loadDataSource(resourceName, false);
|
||||
field.set(srcObj, source);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e);
|
||||
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + srcObj + " error", e);
|
||||
}
|
||||
}, DataSource.class);
|
||||
|
||||
//------------------------------------- 注册 CacheSource --------------------------------------------------------
|
||||
resourceFactory.register(new ResourceFactory.ResourceLoader() {
|
||||
resourceFactory.register(new ResourceTypeLoader() {
|
||||
@Override
|
||||
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
|
||||
public void load(ResourceFactory rf, String srcResourceName, final Object srcObj, final String resourceName, Field field, final Object attachment) {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if (!(src instanceof Service)) throw new RuntimeException("CacheSource must be inject in Service, cannot " + src);
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource
|
||||
final Service srcService = (Service) src;
|
||||
if (!(srcObj instanceof Service)) throw new RuntimeException("CacheSource must be inject in Service, cannot " + srcObj);
|
||||
if ((srcObj instanceof Service) && Sncp.isRemote((Service) srcObj)) return; //远程模式不需要注入 CacheSource
|
||||
final Service srcService = (Service) srcObj;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
SimpleEntry<Class, AnyValue> resEntry = cacheResource.get(resourceName);
|
||||
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
|
||||
if (sourceConf == null) {
|
||||
SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName);
|
||||
sourceConf = resEntry2 == null ? null : resEntry2.getValue();
|
||||
}
|
||||
Class sourceType0 = CacheMemorySource.class;
|
||||
if (sourceConf != null) {
|
||||
String classval = sourceConf.getValue("value");
|
||||
if (classval == null || classval.isEmpty()) {
|
||||
RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class);
|
||||
List<CacheSourceProvider> providers = new ArrayList<>();
|
||||
Iterator<CacheSourceProvider> it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator();
|
||||
while (it.hasNext()) {
|
||||
CacheSourceProvider s = it.next();
|
||||
if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName());
|
||||
if (s != null && s.acceptsConf(sourceConf)) {
|
||||
providers.add(s);
|
||||
}
|
||||
}
|
||||
Collections.sort(providers, (a, b) -> {
|
||||
Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class);
|
||||
Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class);
|
||||
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
});
|
||||
for (CacheSourceProvider provider : providers) {
|
||||
sourceType0 = provider.sourceClass();
|
||||
if (sourceType0 != null) break;
|
||||
}
|
||||
} else {
|
||||
sourceType0 = serverClassLoader.loadClass(classval);
|
||||
}
|
||||
}
|
||||
final Class sourceType = sourceType0;
|
||||
Object source = null;
|
||||
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
|
||||
source = sourceType == CacheMemorySource.class ? new CacheMemorySource(resourceName)
|
||||
: (Modifier.isFinal(sourceType.getModifiers()) || sourceType.getAnnotation(Local.class) != null) ? sourceType.getConstructor().newInstance()
|
||||
: (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
|
||||
Type genericType = field.getGenericType();
|
||||
application.cacheSources.add((CacheSource) source);
|
||||
appResFactory.register(resourceName, CacheSource.class, source);
|
||||
if (genericType != CacheSource.class) {
|
||||
appResFactory.register(resourceName, genericType, source);
|
||||
}
|
||||
}
|
||||
field.set(src, source);
|
||||
rf.inject(source, self); //
|
||||
if (!application.isCompileMode() && source instanceof Service) ((Service) source).init(sourceConf);
|
||||
final boolean ws = (srcObj instanceof org.redkale.net.http.WebSocketNodeService) && sncpAddr != null;
|
||||
CacheSource source = application.loadCacheSource(resourceName, ws);
|
||||
field.set(srcObj, source);
|
||||
|
||||
if ((src instanceof org.redkale.net.http.WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource
|
||||
if (ws) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource
|
||||
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
|
||||
if (source != null && source.getClass().getAnnotation(Local.class) == null) { //本地模式的Service不生成SncpServlet
|
||||
sncpServer.getSncpServer().addSncpServlet((Service) source);
|
||||
}
|
||||
//logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
|
||||
}
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load CacheSource (type = " + source.getClass().getSimpleName() + ", resourceName = '" + resourceName + "')");
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load CacheSource (type = " + (source == null ? null : source.getClass().getSimpleName()) + ", resourceName = '" + resourceName + "')");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
||||
}
|
||||
@@ -458,28 +310,28 @@ public abstract class NodeServer {
|
||||
}, CacheSource.class);
|
||||
|
||||
//------------------------------------- 注册 WebSocketNode --------------------------------------------------------
|
||||
resourceFactory.register(new ResourceFactory.ResourceLoader() {
|
||||
resourceFactory.register(new ResourceTypeLoader() {
|
||||
@Override
|
||||
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
|
||||
public void load(ResourceFactory rf, String srcResourceName, final Object srcObj, final String resourceName, Field field, final Object attachment) {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 WebSocketNode
|
||||
if ((srcObj instanceof Service) && Sncp.isRemote((Service) srcObj)) return; //远程模式不需要注入 WebSocketNode
|
||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||
if (nodeService == null) {
|
||||
final HashSet<String> groups = new HashSet<>();
|
||||
if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup);
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, org.redkale.net.http.WebSocketNodeService.class, Sncp.getMessageAgent((Service) src), application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, org.redkale.net.http.WebSocketNodeService.class, Sncp.getMessageAgent((Service) srcObj), application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
|
||||
(isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService);
|
||||
((org.redkale.net.http.WebSocketNodeService) nodeService).setName(resourceName);
|
||||
}
|
||||
resourceFactory.inject(nodeService, self);
|
||||
MessageAgent messageAgent = Sncp.getMessageAgent((Service) src);
|
||||
resourceFactory.inject(resourceName, nodeService, self);
|
||||
MessageAgent messageAgent = Sncp.getMessageAgent((Service) srcObj);
|
||||
if (messageAgent != null && Sncp.getMessageAgent(nodeService) == null) Sncp.setMessageAgent(nodeService, messageAgent);
|
||||
field.set(src, nodeService);
|
||||
field.set(srcObj, nodeService);
|
||||
if (Sncp.isRemote(nodeService)) {
|
||||
remoteServices.add(nodeService);
|
||||
} else {
|
||||
rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
|
||||
rf.inject(resourceName, nodeService); //动态加载的Service也存在按需加载的注入资源
|
||||
localServices.add(nodeService);
|
||||
interceptorServices.add(nodeService);
|
||||
if (consumer != null) consumer.accept(null, nodeService);
|
||||
@@ -529,7 +381,7 @@ public abstract class NodeServer {
|
||||
|| (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置
|
||||
|| serviceImplClass.getAnnotation(Local.class) != null;//本地模式
|
||||
if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
|
||||
final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
||||
final ResourceTypeLoader resourceLoader = (ResourceFactory rf, String srcResourceName, final Object srcObj, final String resourceName, Field field, final Object attachment) -> {
|
||||
try {
|
||||
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的, 通常为BaseService
|
||||
if (!serviceImplClass.getName().startsWith("org.redkale.") && !serviceImplClass.getSimpleName().contains("Base")) {
|
||||
@@ -545,7 +397,7 @@ public abstract class NodeServer {
|
||||
}
|
||||
|
||||
Service service;
|
||||
final boolean ws = src instanceof WebSocketServlet;
|
||||
final boolean ws = srcObj instanceof WebSocketServlet;
|
||||
if (ws || localed) { //本地模式
|
||||
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, agent, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
|
||||
} else {
|
||||
@@ -565,7 +417,7 @@ public abstract class NodeServer {
|
||||
remoteServices.add(service);
|
||||
if (agent != null) sncpRemoteAgents.put(agent.getName(), agent);
|
||||
} else {
|
||||
if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
|
||||
if (field != null) rf.inject(resourceName, service); //动态加载的Service也存在按需加载的注入资源
|
||||
localServices.add(service);
|
||||
interceptorServices.add(service);
|
||||
if (consumer != null) consumer.accept(agent, service);
|
||||
@@ -577,10 +429,12 @@ public abstract class NodeServer {
|
||||
}
|
||||
};
|
||||
if (entry.isExpect()) {
|
||||
ResourceType rty = entry.getType().getAnnotation(ResourceType.class);
|
||||
resourceFactory.register(resourceLoader, rty == null ? entry.getType() : rty.value());
|
||||
Class t = ResourceFactory.getResourceType(entry.getType());
|
||||
if (resourceFactory.findResourceTypeLoader(t) == null) {
|
||||
resourceFactory.register(resourceLoader, t);
|
||||
}
|
||||
} else {
|
||||
resourceLoader.load(resourceFactory, null, entry.getName(), null, false);
|
||||
resourceLoader.load(resourceFactory, null, null, entry.getName(), null, false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -591,10 +445,10 @@ public abstract class NodeServer {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
//---------------- inject ----------------
|
||||
new ArrayList<>(localServices).forEach(y -> {
|
||||
resourceFactory.inject(y, NodeServer.this);
|
||||
resourceFactory.inject(Sncp.getResourceName(y), y, NodeServer.this);
|
||||
});
|
||||
new ArrayList<>(remoteServices).forEach(y -> {
|
||||
resourceFactory.inject(y, NodeServer.this);
|
||||
resourceFactory.inject(Sncp.getResourceName(y), y, NodeServer.this);
|
||||
calcMaxLength(y);
|
||||
});
|
||||
|
||||
@@ -852,9 +706,9 @@ public abstract class NodeServer {
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
public void command(String cmd) throws IOException {
|
||||
public List<Object> command(String cmd, String[] params) throws IOException {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
List<Object> results = new ArrayList<>();
|
||||
localServices.forEach(y -> {
|
||||
Set<Method> methods = new HashSet<>();
|
||||
Class loop = y.getClass();
|
||||
@@ -862,10 +716,23 @@ public abstract class NodeServer {
|
||||
for (Method m : loop.getMethods()) {
|
||||
Command c = m.getAnnotation(Command.class);
|
||||
if (c == null) continue;
|
||||
if (Modifier.isStatic(m.getModifiers())) continue;
|
||||
if (m.getReturnType() != void.class) continue;
|
||||
if (m.getParameterCount() != 1) continue;
|
||||
if (m.getParameterTypes()[0] != String.class) continue;
|
||||
if (Modifier.isStatic(m.getModifiers())) {
|
||||
logger.log(Level.WARNING, m + " is static on @Command");
|
||||
continue;
|
||||
}
|
||||
if (m.getParameterCount() != 1 && m.getParameterCount() != 2) {
|
||||
logger.log(Level.WARNING, m + " parameter count = " + m.getParameterCount() + " on @Command");
|
||||
continue;
|
||||
}
|
||||
if (m.getParameterTypes()[0] != String.class) {
|
||||
logger.log(Level.WARNING, m + " parameters[0] type is not String.class on @Command");
|
||||
continue;
|
||||
}
|
||||
if (m.getParameterCount() == 2 && m.getParameterTypes()[1] != String[].class) {
|
||||
logger.log(Level.WARNING, m + " parameters[1] type is not String[].class on @Command");
|
||||
continue;
|
||||
}
|
||||
if (!c.value().isEmpty() && !c.value().equalsIgnoreCase(cmd)) continue;
|
||||
methods.add(m);
|
||||
}
|
||||
//} while ((loop = loop.getSuperclass()) != Object.class);
|
||||
@@ -875,7 +742,8 @@ public abstract class NodeServer {
|
||||
try {
|
||||
for (Method method : methods) {
|
||||
one = method;
|
||||
method.invoke(y, cmd);
|
||||
Object r = method.getParameterCount() == 2 ? method.invoke(y, cmd, params) : method.invoke(y, cmd);
|
||||
if (r != null) results.add(r);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, one + " run error, cmd = " + cmd, ex);
|
||||
@@ -886,6 +754,7 @@ public abstract class NodeServer {
|
||||
}
|
||||
});
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
return results;
|
||||
}
|
||||
|
||||
public <T extends Server> T getServer() {
|
||||
|
||||
26
src/main/java/org/redkale/boot/PropertiesAgent.java
Normal file
26
src/main/java/org/redkale/boot/PropertiesAgent.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 配置源Agent, 在init方法内需要实现读取配置信息,如果支持配置更改通知,也需要在init里实现监听
|
||||
*
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public abstract class PropertiesAgent {
|
||||
|
||||
public void compile(AnyValue conf) {
|
||||
}
|
||||
|
||||
public abstract void init(ResourceFactory factory, Properties appProperties, AnyValue conf);
|
||||
|
||||
public abstract void destroy(AnyValue conf);
|
||||
}
|
||||
@@ -37,24 +37,26 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
|
||||
protected ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
//可能被HttpMessageClient用到的服务 key: servicename
|
||||
//可能被HttpMessageClient用到的服务 key: serviceName
|
||||
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> httpAddressMap = new ConcurrentHashMap<>();
|
||||
|
||||
//可能被mqtp用到的服务 key: servicename
|
||||
//可能被mqtp用到的服务 key: serviceName
|
||||
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> mqtpAddressMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) {
|
||||
super.init(config);
|
||||
public void init(ResourceFactory factory, AnyValue config) {
|
||||
super.init(factory, config);
|
||||
|
||||
this.sourceName = getSourceName();
|
||||
|
||||
AnyValue[] properties = config.getAnyValues("property");
|
||||
for (AnyValue property : properties) {
|
||||
if ("ttls".equalsIgnoreCase(property.getValue("name"))) {
|
||||
this.ttls = Integer.parseInt(property.getValue("value", "").trim());
|
||||
if (this.ttls < 5) this.ttls = 10;
|
||||
}
|
||||
}
|
||||
this.ttls = config.getIntValue("ttls", 10);
|
||||
if (this.ttls < 5) this.ttls = 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(AnyValue config) {
|
||||
super.setConfig(config);
|
||||
this.sourceName = getSourceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,15 +65,7 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
}
|
||||
|
||||
public String getSourceName() {
|
||||
AnyValue[] properties = config.getAnyValues("property");
|
||||
for (AnyValue property : properties) {
|
||||
if ("source".equalsIgnoreCase(property.getValue("name"))
|
||||
&& property.getValue("value") != null) {
|
||||
this.sourceName = property.getValue("value");
|
||||
return this.sourceName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return config.getValue("source");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,13 +76,7 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
@Override //ServiceLoader时判断配置是否符合当前实现类
|
||||
public boolean acceptsConf(AnyValue config) {
|
||||
if (config == null) return false;
|
||||
AnyValue[] properties = config.getAnyValues("property");
|
||||
if (properties == null || properties.length == 0) return false;
|
||||
for (AnyValue property : properties) {
|
||||
if ("source".equalsIgnoreCase(property.getValue("name"))
|
||||
&& property.getValue("value") != null) return true;
|
||||
}
|
||||
return false;
|
||||
return config.getValue("source") != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,22 +108,22 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
|
||||
protected void loadMqtpAddressHealth() {
|
||||
List<String> keys = source.queryKeysStartsWith("cluster.mqtp:");
|
||||
keys.forEach(servicename -> {
|
||||
keys.forEach(serviceName -> {
|
||||
try {
|
||||
this.mqtpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS));
|
||||
this.mqtpAddressMap.put(serviceName, queryAddress(serviceName).get(3, TimeUnit.SECONDS));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + servicename + " error", e);
|
||||
logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + serviceName + " error", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void checkHttpAddressHealth() {
|
||||
try {
|
||||
this.httpAddressMap.keySet().stream().forEach(servicename -> {
|
||||
this.httpAddressMap.keySet().stream().forEach(serviceName -> {
|
||||
try {
|
||||
this.httpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS));
|
||||
this.httpAddressMap.put(serviceName, queryAddress(serviceName).get(3, TimeUnit.SECONDS));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "checkHttpAddressHealth check " + servicename + " error", e);
|
||||
logger.log(Level.SEVERE, "checkHttpAddressHealth check " + serviceName + " error", e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
@@ -148,10 +136,10 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
newaddr.addr = entry.address;
|
||||
newaddr.nodeid = this.nodeid;
|
||||
newaddr.time = System.currentTimeMillis();
|
||||
source.hset(entry.checkname, entry.checkid, AddressEntry.class, newaddr);
|
||||
source.hset(entry.checkName, entry.checkid, AddressEntry.class, newaddr);
|
||||
}
|
||||
|
||||
@Override //获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段
|
||||
@Override //获取MQTP的HTTP远程服务的可用ip列表, key = serviceName的后半段
|
||||
public CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname) {
|
||||
final Map<String, Collection<InetSocketAddress>> rsmap = new ConcurrentHashMap<>();
|
||||
final String servicenamprefix = generateHttpServiceName(protocol, module, null) + ":";
|
||||
@@ -162,22 +150,22 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
|
||||
@Override //获取HTTP远程服务的可用ip列表
|
||||
public CompletableFuture<Collection<InetSocketAddress>> queryHttpAddress(String protocol, String module, String resname) {
|
||||
final String servicename = generateHttpServiceName(protocol, module, resname);
|
||||
Collection<InetSocketAddress> rs = httpAddressMap.get(servicename);
|
||||
final String serviceName = generateHttpServiceName(protocol, module, resname);
|
||||
Collection<InetSocketAddress> rs = httpAddressMap.get(serviceName);
|
||||
if (rs != null) return CompletableFuture.completedFuture(rs);
|
||||
return queryAddress(servicename).thenApply(t -> {
|
||||
httpAddressMap.put(servicename, t);
|
||||
return queryAddress(serviceName).thenApply(t -> {
|
||||
httpAddressMap.put(serviceName, t);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Collection<InetSocketAddress>> queryAddress(final ClusterEntry entry) {
|
||||
return queryAddress(entry.servicename);
|
||||
return queryAddress(entry.serviceName);
|
||||
}
|
||||
|
||||
private CompletableFuture<Collection<InetSocketAddress>> queryAddress(final String servicename) {
|
||||
final CompletableFuture<Map<String, AddressEntry>> future = source.hmapAsync(servicename, AddressEntry.class, 0, 10000);
|
||||
private CompletableFuture<Collection<InetSocketAddress>> queryAddress(final String serviceName) {
|
||||
final CompletableFuture<Map<String, AddressEntry>> future = source.hmapAsync(serviceName, AddressEntry.class, 0, 10000);
|
||||
return future.thenApply(map -> {
|
||||
final Set<InetSocketAddress> set = new HashSet<>();
|
||||
map.forEach((n, v) -> {
|
||||
@@ -188,9 +176,9 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
}
|
||||
|
||||
protected boolean isApplicationHealth() {
|
||||
String servicename = generateApplicationServiceName();
|
||||
String serviceName = generateApplicationServiceName();
|
||||
String serviceid = generateApplicationServiceId();
|
||||
AddressEntry entry = (AddressEntry) source.hget(servicename, serviceid, AddressEntry.class);
|
||||
AddressEntry entry = (AddressEntry) source.hget(serviceName, serviceid, AddressEntry.class);
|
||||
return entry != null && (System.currentTimeMillis() - entry.time) / 1000 < ttls;
|
||||
}
|
||||
|
||||
@@ -210,18 +198,18 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
deregister(application);
|
||||
|
||||
String serviceid = generateApplicationServiceId();
|
||||
String servicename = generateApplicationServiceName();
|
||||
String serviceName = generateApplicationServiceName();
|
||||
AddressEntry entry = new AddressEntry();
|
||||
entry.addr = this.appAddress;
|
||||
entry.nodeid = this.nodeid;
|
||||
entry.time = System.currentTimeMillis();
|
||||
source.hset(servicename, serviceid, AddressEntry.class, entry);
|
||||
source.hset(serviceName, serviceid, AddressEntry.class, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregister(Application application) {
|
||||
String servicename = generateApplicationServiceName();
|
||||
source.remove(servicename);
|
||||
String serviceName = generateApplicationServiceName();
|
||||
source.remove(serviceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -233,7 +221,7 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
entry.addr = clusterEntry.address;
|
||||
entry.nodeid = this.nodeid;
|
||||
entry.time = System.currentTimeMillis();
|
||||
source.hset(clusterEntry.servicename, clusterEntry.serviceid, AddressEntry.class, entry);
|
||||
source.hset(clusterEntry.serviceName, clusterEntry.serviceid, AddressEntry.class, entry);
|
||||
return clusterEntry;
|
||||
}
|
||||
|
||||
@@ -243,24 +231,24 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
}
|
||||
|
||||
protected void deregister(NodeServer ns, String protocol, Service service, boolean realcanceled) {
|
||||
String servicename = generateServiceName(ns, protocol, service);
|
||||
String serviceName = generateServiceName(ns, protocol, service);
|
||||
String serviceid = generateServiceId(ns, protocol, service);
|
||||
ClusterEntry currEntry = null;
|
||||
for (final ClusterEntry entry : localEntrys.values()) {
|
||||
if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) {
|
||||
if (entry.serviceName.equals(serviceName) && entry.serviceid.equals(serviceid)) {
|
||||
currEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currEntry == null) {
|
||||
for (final ClusterEntry entry : remoteEntrys.values()) {
|
||||
if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) {
|
||||
if (entry.serviceName.equals(serviceName) && entry.serviceid.equals(serviceid)) {
|
||||
currEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
source.hremove(servicename, serviceid);
|
||||
source.hremove(serviceName, serviceid);
|
||||
if (realcanceled && currEntry != null) currEntry.canceled = true;
|
||||
if (!"mqtp".equals(protocol) && currEntry != null && currEntry.submqtp) {
|
||||
deregister(ns, "mqtp", service, realcanceled);
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
package org.redkale.cluster;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.*;
|
||||
import static org.redkale.boot.Application.*;
|
||||
import org.redkale.convert.ConvertDisabled;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.mq.MessageMultiConsumer;
|
||||
import org.redkale.net.*;
|
||||
@@ -60,7 +62,7 @@ public abstract class ClusterAgent {
|
||||
|
||||
protected final ConcurrentHashMap<String, ClusterEntry> remoteEntrys = new ConcurrentHashMap<>();
|
||||
|
||||
public void init(AnyValue config) {
|
||||
public void init(ResourceFactory factory, AnyValue config) {
|
||||
this.config = config;
|
||||
this.name = config.getValue("name", "");
|
||||
this.waits = config.getBoolValue("waits", false);
|
||||
@@ -106,7 +108,7 @@ public abstract class ClusterAgent {
|
||||
if (localServices.isEmpty()) return;
|
||||
//注册本地模式
|
||||
for (Service service : localServices) {
|
||||
if (!canRegister(protocol, service)) continue;
|
||||
if (!canRegister(ns, protocol, service)) continue;
|
||||
ClusterEntry htentry = register(ns, protocol, service);
|
||||
localEntrys.put(htentry.serviceid, htentry);
|
||||
if (protocol.toLowerCase().startsWith("http")) {
|
||||
@@ -132,19 +134,21 @@ public abstract class ClusterAgent {
|
||||
public void deregister(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
|
||||
//注销本地模式 远程模式不注册
|
||||
for (Service service : localServices) {
|
||||
if (!canRegister(protocol, service)) continue;
|
||||
if (!canRegister(ns, protocol, service)) continue;
|
||||
deregister(ns, protocol, service);
|
||||
}
|
||||
afterDeregister(ns, protocol);
|
||||
}
|
||||
|
||||
protected boolean canRegister(String protocol, Service service) {
|
||||
protected boolean canRegister(NodeServer ns, String protocol, Service service) {
|
||||
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false;
|
||||
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
|
||||
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return false;
|
||||
if (service instanceof WebSocketNode) {
|
||||
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
|
||||
}
|
||||
ClusterEntry entry = new ClusterEntry(ns, protocol, service);
|
||||
if (entry.serviceName.trim().endsWith(serviceSeparator())) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -167,7 +171,7 @@ public abstract class ClusterAgent {
|
||||
return 10;
|
||||
}
|
||||
|
||||
//获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段
|
||||
//获取MQTP的HTTP远程服务的可用ip列表, key = serviceName的后半段
|
||||
public abstract CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname);
|
||||
|
||||
//获取HTTP远程服务的可用ip列表
|
||||
@@ -184,17 +188,25 @@ public abstract class ClusterAgent {
|
||||
|
||||
//格式: protocol:classtype-resourcename
|
||||
protected void updateSncpTransport(ClusterEntry entry) {
|
||||
Service service = entry.serviceref.get();
|
||||
Service service = entry.serviceRef.get();
|
||||
if (service == null) return;
|
||||
Collection<InetSocketAddress> addrs = ClusterAgent.this.queryAddress(entry).join();
|
||||
Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netprotocol, entry.address, null, addrs);
|
||||
Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netProtocol, entry.address, null, addrs);
|
||||
}
|
||||
|
||||
protected String urlEncode(String value) {
|
||||
return value == null ? null : URLEncoder.encode(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
protected String generateApplicationServiceName() {
|
||||
return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node" + this.nodeid;
|
||||
return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node." + this.nodeid;
|
||||
}
|
||||
|
||||
protected String generateApplicationServiceId() { //与servicename相同
|
||||
protected String generateApplicationServiceType() {
|
||||
return "application.nodes";
|
||||
}
|
||||
|
||||
protected String generateApplicationServiceId() { //与serviceName相同
|
||||
return generateApplicationServiceName();
|
||||
}
|
||||
|
||||
@@ -206,9 +218,21 @@ public abstract class ClusterAgent {
|
||||
return "check-" + generateApplicationServiceId();
|
||||
}
|
||||
|
||||
protected String generateApplicationHost() {
|
||||
return this.appAddress.getHostString();
|
||||
}
|
||||
|
||||
protected int generateApplicationPort() {
|
||||
return this.appAddress.getPort();
|
||||
}
|
||||
|
||||
protected String serviceSeparator() {
|
||||
return "-";
|
||||
}
|
||||
|
||||
//也会提供给HttpMessageClusterAgent适用
|
||||
public String generateHttpServiceName(String protocol, String module, String resname) {
|
||||
return protocol.toLowerCase() + ":" + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
|
||||
return protocol.toLowerCase() + serviceSeparator() + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
|
||||
}
|
||||
|
||||
//格式: protocol:classtype-resourcename
|
||||
@@ -216,21 +240,21 @@ public abstract class ClusterAgent {
|
||||
if (protocol.toLowerCase().startsWith("http")) { //HTTP使用RestService.name方式是为了与MessageClient中的module保持一致, 因为HTTP依靠的url中的module,无法知道Service类名
|
||||
String resname = Sncp.getResourceName(service);
|
||||
String module = Rest.getRestModule(service).toLowerCase();
|
||||
return protocol.toLowerCase() + ":" + module + (resname.isEmpty() ? "" : ("-" + resname));
|
||||
return protocol.toLowerCase() + serviceSeparator() + module + (resname.isEmpty() ? "" : ("-" + resname));
|
||||
}
|
||||
if ("mqtp".equalsIgnoreCase(protocol)) {
|
||||
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
|
||||
String selfmodule = Rest.getRestModule(service).toLowerCase();
|
||||
return protocol.toLowerCase() + ":" + mmc.module() + ":" + selfmodule;
|
||||
return protocol.toLowerCase() + serviceSeparator() + mmc.module() + serviceSeparator() + selfmodule;
|
||||
}
|
||||
if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + ":" + service.getClass().getName();
|
||||
if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + serviceSeparator() + service.getClass().getName();
|
||||
String resname = Sncp.getResourceName(service);
|
||||
return protocol.toLowerCase() + ":" + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname));
|
||||
return protocol.toLowerCase() + serviceSeparator() + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname));
|
||||
}
|
||||
|
||||
//格式: protocol:classtype-resourcename:nodeid
|
||||
protected String generateServiceId(NodeServer ns, String protocol, Service service) {
|
||||
return generateServiceName(ns, protocol, service) + ":" + this.nodeid;
|
||||
return generateServiceName(ns, protocol, service) + serviceSeparator() + this.nodeid;
|
||||
}
|
||||
|
||||
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
|
||||
@@ -249,11 +273,6 @@ public abstract class ClusterAgent {
|
||||
return remoteEntrys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
|
||||
public TransportFactory getTransportFactory() {
|
||||
return transportFactory;
|
||||
}
|
||||
@@ -296,19 +315,26 @@ public abstract class ClusterAgent {
|
||||
|
||||
public class ClusterEntry {
|
||||
|
||||
//serviceName+nodeid为主 服务的单个实例
|
||||
public String serviceid;
|
||||
|
||||
public String servicename;
|
||||
//以协议+Rest资源名为主 服务类名
|
||||
public String serviceName;
|
||||
|
||||
public String serviceType;
|
||||
|
||||
public String checkid;
|
||||
|
||||
public String checkname;
|
||||
public String checkName;
|
||||
|
||||
//http or sncp or mqtp
|
||||
public String protocol;
|
||||
|
||||
public String netprotocol;
|
||||
//TCP or UDP
|
||||
public String netProtocol;
|
||||
|
||||
public WeakReference<Service> serviceref;
|
||||
@ConvertDisabled
|
||||
public WeakReference<Service> serviceRef;
|
||||
|
||||
public InetSocketAddress address;
|
||||
|
||||
@@ -318,9 +344,10 @@ public abstract class ClusterAgent {
|
||||
|
||||
public ClusterEntry(NodeServer ns, String protocol, Service service) {
|
||||
this.serviceid = generateServiceId(ns, protocol, service);
|
||||
this.servicename = generateServiceName(ns, protocol, service);
|
||||
this.serviceName = generateServiceName(ns, protocol, service);
|
||||
this.checkid = generateCheckId(ns, protocol, service);
|
||||
this.checkname = generateCheckName(ns, protocol, service);
|
||||
this.checkName = generateCheckName(ns, protocol, service);
|
||||
this.serviceType = Sncp.getServiceType(service).getName();
|
||||
this.protocol = protocol;
|
||||
InetSocketAddress addr = ns.getSocketAddress();
|
||||
String host = addr.getHostString();
|
||||
@@ -329,9 +356,9 @@ public abstract class ClusterAgent {
|
||||
addr = new InetSocketAddress(host, addr.getPort());
|
||||
}
|
||||
this.address = addr;
|
||||
this.serviceref = new WeakReference(service);
|
||||
this.serviceRef = new WeakReference(service);
|
||||
Server server = ns.getServer();
|
||||
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL;
|
||||
this.netProtocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。
|
||||
*
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -46,4 +46,8 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.function.IntFunction;
|
||||
import org.redkale.util.Creator;
|
||||
|
||||
/**
|
||||
* 数组的反序列化操作类 <br>
|
||||
@@ -30,6 +32,8 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
|
||||
protected final Decodeable<Reader, T> componentDecoder;
|
||||
|
||||
protected final IntFunction<T[]> componentArrayFunction;
|
||||
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
@@ -52,6 +56,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
factory.register(type, this);
|
||||
this.componentDecoder = factory.loadDecoder(this.componentType);
|
||||
this.componentArrayFunction = Creator.arrayFunction(this.componentClass);
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
@@ -102,7 +107,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
}
|
||||
in.readArrayE();
|
||||
T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size());
|
||||
T[] rs = this.componentArrayFunction.apply(result.size());
|
||||
return result.toArray(rs);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
|
||||
protected final Encodeable<Writer, Object> componentEncoder;
|
||||
|
||||
protected final boolean subtypefinal;
|
||||
protected final boolean subTypeFinal;
|
||||
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
@@ -49,7 +49,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
factory.register(type, this);
|
||||
this.componentEncoder = factory.loadEncoder(this.componentType);
|
||||
this.anyEncoder = factory.getAnyEncoder();
|
||||
this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
|
||||
this.subTypeFinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
@@ -74,7 +74,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
if (this.componentEncoder == null) {
|
||||
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
|
||||
if (itemEncoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -85,8 +86,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
|
||||
if (subtypefinal) {
|
||||
if (subTypeFinal) {
|
||||
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
|
||||
for (int i = 0;; i++) {
|
||||
writeMemberValue(out, member, itemEncoder, value[i], i);
|
||||
@@ -122,6 +122,11 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
@@ -129,5 +134,4 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
public Encodeable<Writer, Object> getComponentEncoder() {
|
||||
return componentEncoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,6 +96,11 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.type + ", encoder:" + this.componentEncoder + "}";
|
||||
|
||||
77
src/main/java/org/redkale/convert/ConvertCoder.java
Normal file
77
src/main/java/org/redkale/convert/ConvertCoder.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
|
||||
/**
|
||||
* 依附在setter、getter方法、字段进行简单的配置 <br>
|
||||
* 优先使用coder字段
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD, FIELD})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(ConvertCoder.ConvertCoders.class)
|
||||
public @interface ConvertCoder {
|
||||
|
||||
/**
|
||||
* 需要指定的字段类型,指定了coder字段值则可以不设置此字段
|
||||
*
|
||||
* @return 字段类名
|
||||
*/
|
||||
Class column() default Object.class;
|
||||
|
||||
/**
|
||||
* 解析/序列化定制化的SimpledCoder
|
||||
*
|
||||
* @return SimpledCoder类
|
||||
*/
|
||||
Class<? extends SimpledCoder> coder() default SimpledCoder.class;
|
||||
|
||||
/**
|
||||
* 序列化定制化的 Encodeable
|
||||
*
|
||||
* @return Encodeable 类
|
||||
*/
|
||||
Class<? extends Encodeable> encoder() default Encodeable.class;
|
||||
|
||||
/**
|
||||
* 反序列化定制化的 Decodeable
|
||||
*
|
||||
* @return Decodeable 类
|
||||
*/
|
||||
Class<? extends Decodeable> decoder() default Decodeable.class;
|
||||
|
||||
/**
|
||||
* 解析/序列化定制化的TYPE
|
||||
*
|
||||
* @return JSON or BSON or ALL
|
||||
*/
|
||||
ConvertType type() default ConvertType.ALL;
|
||||
|
||||
/**
|
||||
* ConvertCoder 的多用类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD, FIELD})
|
||||
@Retention(RUNTIME)
|
||||
public static @interface ConvertCoders {
|
||||
|
||||
ConvertCoder[] value();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.*;
|
||||
@@ -248,7 +248,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
if (type instanceof Class) {
|
||||
Class<?> clazz = (Class) type;
|
||||
ConvertImpl ci = clazz.getAnnotation(ConvertImpl.class);
|
||||
if (ci != null) {
|
||||
if (ci != null && ci.value() != Object.class) {
|
||||
if (!clazz.isAssignableFrom(ci.value())) {
|
||||
throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + ".value(" + ci.value() + ") must be " + clazz + "'s subclass");
|
||||
}
|
||||
if (!Modifier.isAbstract(clazz.getModifiers()) && !Modifier.isInterface(clazz.getModifiers())) {
|
||||
throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + " must at interface or abstract class, but " + clazz + " not");
|
||||
}
|
||||
@@ -270,6 +273,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
return new ObjectDecoder(type);
|
||||
}
|
||||
|
||||
protected <E> Decodeable<R, E> createMultiImplDecoder(Class[] types) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected <E> ObjectEncoder<W, E> createObjectEncoder(Type type) {
|
||||
return new ObjectEncoder(type);
|
||||
}
|
||||
@@ -401,7 +408,9 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
static String readGetSetFieldName(Method method) {
|
||||
if (method == null) return null;
|
||||
String fname = method.getName();
|
||||
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; //record类会直接用field名作为method名
|
||||
if (!(fname.startsWith("is") && fname.length() > 2)
|
||||
&& !(fname.startsWith("get") && fname.length() > 3)
|
||||
&& !(fname.startsWith("set") && fname.length() > 3)) return fname; //record类会直接用field名作为method名
|
||||
fname = fname.substring(fname.startsWith("is") ? 2 : 3);
|
||||
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
|
||||
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
|
||||
@@ -482,6 +491,175 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
}
|
||||
|
||||
ConvertFactory columnFactory(Class type, ConvertCoder[] coders, boolean encode) {
|
||||
if (coders == null || coders.length < 1) return this;
|
||||
final ConvertType ct = this.getConvertType();
|
||||
List<Encodeable> encoderList = null;
|
||||
List<Decodeable> decoderList = null;
|
||||
Class readerOrWriterClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[encode ? 1 : 0];
|
||||
for (ConvertCoder ann : coders) {
|
||||
if (!ann.type().contains(ct)) continue;
|
||||
SimpledCoder coder = null;
|
||||
Class<? extends SimpledCoder> clazz1 = ann.coder();
|
||||
if (clazz1 != SimpledCoder.class) {
|
||||
try {
|
||||
boolean skip = false;
|
||||
RedkaleClassLoader.putReflectionPublicMethods(clazz1.getName());
|
||||
for (Method method : clazz1.getMethods()) {
|
||||
if (method.isBridge()) continue;
|
||||
if (encode) {
|
||||
if ("convertTo".equals(method.getName()) && method.getParameterCount() == 2 && Writer.class.isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
skip = !method.getParameterTypes()[0].isAssignableFrom(readerOrWriterClass);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ("convertFrom".equals(method.getName()) && method.getParameterCount() == 1 && Reader.class.isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
skip = !method.getParameterTypes()[0].isAssignableFrom(readerOrWriterClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (skip) continue;
|
||||
Field instanceField = clazz1.getField("instance");
|
||||
if (Modifier.isStatic(instanceField.getModifiers()) && instanceField.getType() == clazz1) {
|
||||
RedkaleClassLoader.putReflectionField(clazz1.getName(), instanceField);
|
||||
coder = (SimpledCoder) instanceField.get(null);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
if (coder == null) {
|
||||
try {
|
||||
coder = (SimpledCoder) clazz1.getConstructor().newInstance();
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(clazz1, clazz1.getName());
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (encode) {
|
||||
if (encoderList == null) encoderList = new ArrayList<>();
|
||||
encoderList.add(coder);
|
||||
} else {
|
||||
if (decoderList == null) decoderList = new ArrayList<>();
|
||||
decoderList.add(coder);
|
||||
}
|
||||
}
|
||||
if (coder == null) {
|
||||
Class colType = type;
|
||||
if (ann.column() != Object.class) colType = ann.column();
|
||||
if (encode) {
|
||||
Class<? extends Encodeable> clazz2 = ann.encoder();
|
||||
if (clazz2 != Encodeable.class) {
|
||||
try {
|
||||
boolean skip = false;
|
||||
RedkaleClassLoader.putReflectionPublicMethods(clazz2.getName());
|
||||
for (Method method : clazz2.getMethods()) {
|
||||
if (method.isBridge()) continue;
|
||||
if ("convertTo".equals(method.getName()) && method.getParameterCount() == 2 && Writer.class.isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
skip = !method.getParameterTypes()[0].isAssignableFrom(readerOrWriterClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip) continue;
|
||||
Encodeable encoder = null;
|
||||
Constructor constructor = clazz2.getConstructors()[0];
|
||||
Parameter[] params = constructor.getParameters();
|
||||
Class[] paramTypes = new Class[params.length];
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
paramTypes[i] = params[i].getType();
|
||||
}
|
||||
if (params.length == 0) {
|
||||
encoder = (Encodeable) constructor.newInstance();
|
||||
} else if (params.length == 1) {
|
||||
if (paramTypes[0] != Type.class) throw new RuntimeException(clazz2 + " not found public empty-parameter Constructor");
|
||||
encoder = (Encodeable) constructor.newInstance(colType);
|
||||
} else if (params.length == 2) {
|
||||
if (paramTypes[0] == ConvertFactory.class && paramTypes[1] == Type.class) {
|
||||
encoder = (Encodeable) constructor.newInstance(this, colType);
|
||||
} else if (paramTypes[0] == Type.class && paramTypes[1] == ConvertFactory.class) {
|
||||
encoder = (Encodeable) constructor.newInstance(colType, this);
|
||||
} else {
|
||||
throw new RuntimeException(clazz2 + " not found public empty-parameter Constructor");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException(clazz2 + " not found public empty-parameter Constructor");
|
||||
}
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(clazz2, clazz2.getName());
|
||||
if (encoderList == null) encoderList = new ArrayList<>();
|
||||
encoderList.add(encoder);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Class<? extends Decodeable> clazz2 = ann.decoder();
|
||||
if (clazz2 != Decodeable.class) {
|
||||
try {
|
||||
boolean skip = false;
|
||||
RedkaleClassLoader.putReflectionPublicMethods(clazz2.getName());
|
||||
for (Method method : clazz2.getMethods()) {
|
||||
if (method.isBridge()) continue;
|
||||
if ("convertFrom".equals(method.getName()) && method.getParameterCount() == 1 && Reader.class.isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
skip = !method.getParameterTypes()[0].isAssignableFrom(readerOrWriterClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip) continue;
|
||||
Decodeable decoder = null;
|
||||
Constructor constructor = clazz2.getConstructors()[0];
|
||||
Parameter[] params = constructor.getParameters();
|
||||
Class[] paramTypes = new Class[params.length];
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
paramTypes[i] = params[i].getType();
|
||||
}
|
||||
if (params.length == 0) {
|
||||
decoder = (Decodeable) constructor.newInstance();
|
||||
} else if (params.length == 1) {
|
||||
if (paramTypes[0] != Type.class) throw new RuntimeException(clazz2 + " not found public empty-parameter Constructor");
|
||||
decoder = (Decodeable) constructor.newInstance(colType);
|
||||
} else if (params.length == 2) {
|
||||
if (paramTypes[0] == ConvertFactory.class && paramTypes[1] == Type.class) {
|
||||
decoder = (Decodeable) constructor.newInstance(this, colType);
|
||||
} else if (paramTypes[0] == Type.class && paramTypes[1] == ConvertFactory.class) {
|
||||
decoder = (Decodeable) constructor.newInstance(colType, this);
|
||||
} else {
|
||||
throw new RuntimeException(clazz2 + " not found public empty-parameter Constructor");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException(clazz2 + " not found public empty-parameter Constructor");
|
||||
}
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(clazz2, clazz2.getName());
|
||||
if (decoderList == null) decoderList = new ArrayList<>();
|
||||
decoderList.add(decoder);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (encoderList == null && decoderList == null) return this;
|
||||
ConvertFactory child = createChild();
|
||||
if (encode) {
|
||||
for (Encodeable item : encoderList) {
|
||||
child.register(item.getType(), item);
|
||||
if (item instanceof ObjectEncoder) {
|
||||
((ObjectEncoder) item).init(child);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Decodeable item : decoderList) {
|
||||
child.register(item.getType(), item);
|
||||
if (item instanceof ObjectDecoder) {
|
||||
((ObjectDecoder) item).init(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
private Class findEntityAlias(String name) {
|
||||
Class clazz = entitys.get(name);
|
||||
return parent == null ? clazz : parent.findEntityAlias(name);
|
||||
@@ -600,8 +778,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
|
||||
public final void reloadCoder(final Type type, final Class clazz) {
|
||||
this.register(type, this.createDecoder(type, clazz));
|
||||
this.register(type, this.createEncoder(type, clazz));
|
||||
this.register(type, this.createDecoder(type, clazz, false));
|
||||
this.register(type, this.createEncoder(type, clazz, false));
|
||||
}
|
||||
|
||||
public final <E> void register(final Class<E> clazz, final Creator<? extends E> creator) {
|
||||
@@ -717,10 +895,14 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
throw new ConvertException("not support the type (" + type + ")");
|
||||
}
|
||||
//此处不能再findDecoder,否则type与class不一致, 如: RetResult 和 RetResult<Integer>
|
||||
return createDecoder(type, clazz);
|
||||
return createDecoder(type, clazz, false);
|
||||
}
|
||||
|
||||
public final <E> Decodeable<R, E> createDecoder(final Type type) {
|
||||
return createDecoder(type, false);
|
||||
}
|
||||
|
||||
public final <E> Decodeable<R, E> createDecoder(final Type type, boolean skipCustomMethod) {
|
||||
Class clazz;
|
||||
if (type instanceof ParameterizedType) {
|
||||
final ParameterizedType pts = (ParameterizedType) type;
|
||||
@@ -730,10 +912,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
} else {
|
||||
throw new ConvertException("not support the type (" + type + ")");
|
||||
}
|
||||
return createDecoder(type, clazz);
|
||||
return createDecoder(type, clazz, skipCustomMethod);
|
||||
}
|
||||
|
||||
private <E> Decodeable<R, E> createDecoder(final Type type, final Class clazz) {
|
||||
private <E> Decodeable<R, E> createDecoder(final Type type, final Class clazz, boolean skipCustomMethod) {
|
||||
Decodeable<R, E> decoder = null;
|
||||
ObjectDecoder od = null;
|
||||
if (clazz.isEnum()) {
|
||||
@@ -756,25 +938,47 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|| java.util.AbstractMap.SimpleEntry.class == clazz
|
||||
|| clazz.getName().startsWith("java.awt.geom.Point2D")) {
|
||||
Decodeable simpleCoder = null;
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) continue;
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1) continue;
|
||||
if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue;
|
||||
if (!Decodeable.class.isAssignableFrom(method.getReturnType())) continue;
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
simpleCoder = (Decodeable) method.invoke(null, this);
|
||||
RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName());
|
||||
RedkaleClassLoader.putReflectionMethod(clazz.getName(), method);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (!skipCustomMethod) {
|
||||
for (Class subclazz : getSuperClasses(clazz)) {
|
||||
for (final Method method : subclazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) continue;
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1 && paramTypes.length != 2) continue;
|
||||
if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue;
|
||||
if (paramTypes.length == 2 && paramTypes[1] != Class.class && paramTypes[1] != Type.class) continue;
|
||||
if (!Decodeable.class.isAssignableFrom(method.getReturnType())) continue;
|
||||
if (Modifier.isPrivate(method.getModifiers()) && subclazz != clazz) continue; //声明private的只能被自身类使用
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
simpleCoder = (Decodeable) (paramTypes.length == 2 ? (paramTypes[1] == Type.class ? method.invoke(null, this, type) : method.invoke(null, this, clazz)) : method.invoke(null, this));
|
||||
RedkaleClassLoader.putReflectionDeclaredMethods(subclazz.getName());
|
||||
RedkaleClassLoader.putReflectionMethod(subclazz.getName(), method);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (simpleCoder != null) break;
|
||||
}
|
||||
}
|
||||
if (simpleCoder == null) {
|
||||
Type impl = formatObjectType(type);
|
||||
od = createObjectDecoder(impl);
|
||||
decoder = od;
|
||||
if (type instanceof Class) {
|
||||
Class<?> typeclz = (Class) type;
|
||||
ConvertImpl ci = typeclz.getAnnotation(ConvertImpl.class);
|
||||
if (ci != null && ci.types().length > 0) {
|
||||
for (Class sub : ci.types()) {
|
||||
if (!typeclz.isAssignableFrom(sub)) {
|
||||
throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + ".types(" + sub + ") must be " + typeclz + "'s subclass");
|
||||
}
|
||||
}
|
||||
decoder = createMultiImplDecoder(ci.types());
|
||||
}
|
||||
}
|
||||
if (decoder == null) {
|
||||
Type impl = formatObjectType(type);
|
||||
od = createObjectDecoder(impl);
|
||||
decoder = od;
|
||||
}
|
||||
} else {
|
||||
decoder = simpleCoder;
|
||||
}
|
||||
@@ -807,10 +1011,14 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
throw new ConvertException("not support the type (" + type + ")");
|
||||
}
|
||||
//此处不能再findEncoder,否则type与class不一致, 如: RetResult 和 RetResult<Integer>
|
||||
return createEncoder(type, clazz);
|
||||
return createEncoder(type, clazz, false);
|
||||
}
|
||||
|
||||
public final <E> Encodeable<W, E> createEncoder(final Type type) {
|
||||
return createEncoder(type, false);
|
||||
}
|
||||
|
||||
public final <E> Encodeable<W, E> createEncoder(final Type type, boolean skipCustomMethod) {
|
||||
Class clazz;
|
||||
if (type instanceof ParameterizedType) {
|
||||
final ParameterizedType pts = (ParameterizedType) type;
|
||||
@@ -820,10 +1028,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
} else {
|
||||
throw new ConvertException("not support the type (" + type + ")");
|
||||
}
|
||||
return createEncoder(type, clazz);
|
||||
return createEncoder(type, clazz, skipCustomMethod);
|
||||
}
|
||||
|
||||
private <E> Encodeable<W, E> createEncoder(final Type type, final Class clazz) {
|
||||
private <E> Encodeable<W, E> createEncoder(final Type type, final Class clazz, boolean skipCustomMethod) {
|
||||
Encodeable<W, E> encoder = null;
|
||||
ObjectEncoder oe = null;
|
||||
if (clazz.isEnum()) {
|
||||
@@ -843,19 +1051,27 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
} else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz
|
||||
|| java.util.Map.Entry.class == clazz || java.util.AbstractMap.SimpleEntry.class == clazz) {
|
||||
Encodeable simpleCoder = null;
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) continue;
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1) continue;
|
||||
if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue;
|
||||
if (!Encodeable.class.isAssignableFrom(method.getReturnType())) continue;
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
simpleCoder = (Encodeable) method.invoke(null, this);
|
||||
RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName());
|
||||
RedkaleClassLoader.putReflectionMethod(clazz.getName(), method);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (!skipCustomMethod) {
|
||||
for (Class subclazz : getSuperClasses(clazz)) {
|
||||
for (final Method method : subclazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) continue;
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1 && paramTypes.length != 2) continue;
|
||||
if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue;
|
||||
if (paramTypes.length == 2 && paramTypes[1] != Class.class && paramTypes[1] != Type.class) continue;
|
||||
if (!Encodeable.class.isAssignableFrom(method.getReturnType())) continue;
|
||||
if (Modifier.isPrivate(method.getModifiers()) && subclazz != clazz) continue; //声明private的只能被自身类使用
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
simpleCoder = (Encodeable) (paramTypes.length == 2 ? (paramTypes[1] == Type.class ? method.invoke(null, this, type) : method.invoke(null, this, clazz)) : method.invoke(null, this));
|
||||
RedkaleClassLoader.putReflectionDeclaredMethods(subclazz.getName());
|
||||
RedkaleClassLoader.putReflectionMethod(subclazz.getName(), method);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (simpleCoder != null) break;
|
||||
}
|
||||
}
|
||||
if (simpleCoder == null) {
|
||||
@@ -876,4 +1092,18 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|
||||
}
|
||||
|
||||
private Set<Class> getSuperClasses(final Class clazz) {
|
||||
Set<Class> set = new LinkedHashSet<>();
|
||||
set.add(clazz);
|
||||
Class recursClass = clazz;
|
||||
while ((recursClass = recursClass.getSuperclass()) != null) {
|
||||
if (recursClass == Object.class) break;
|
||||
set.addAll(getSuperClasses(recursClass));
|
||||
}
|
||||
for (Class sub : clazz.getInterfaces()) {
|
||||
set.addAll(getSuperClasses(sub));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,18 +19,18 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
* public String getName();
|
||||
* }
|
||||
*
|
||||
*
|
||||
*
|
||||
* public class OneImpl implements OneEntity {
|
||||
* private String name;
|
||||
* public String getName(){return name;}
|
||||
* public void setName(String name){this.name=name;}
|
||||
* }
|
||||
*
|
||||
*
|
||||
*
|
||||
* String json = "{'name':'hello'}";
|
||||
* OneEntity one = JsonConvert.root.convertFrom(OneEntity.class, json);
|
||||
* OneEntity one = JsonConvert.root().convertFrom(OneEntity.class, json);
|
||||
* //one instanceof OneImpl
|
||||
*
|
||||
*
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -38,7 +38,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
* @author zhangjx
|
||||
* @since 2.5.0
|
||||
*/
|
||||
@Inherited
|
||||
//一定不能标记Inherited
|
||||
@Documented
|
||||
@Target({TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@@ -49,5 +49,12 @@ public @interface ConvertImpl {
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
Class value();
|
||||
Class value() default Object.class;
|
||||
|
||||
/**
|
||||
* 实现类的集合
|
||||
*
|
||||
* @return Class[]
|
||||
*/
|
||||
Class[] types() default {};
|
||||
}
|
||||
|
||||
@@ -88,26 +88,26 @@ public final class DeMember<R extends Reader, T, F> {
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
public static <R extends Reader, T, F> DeMember<R, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldname) {
|
||||
public static <R extends Reader, T, F> DeMember<R, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldName) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldname);
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
return new DeMember<>(Attribute.create(field), factory.loadDecoder(field.getGenericType()), field, null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <R extends Reader, T, F> DeMember<R, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldname, final Class<F> fieldtype) {
|
||||
public static <R extends Reader, T, F> DeMember<R, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldName, final Class<F> fieldType) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldname);
|
||||
return new DeMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadDecoder(fieldtype), field, null);
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
return new DeMember<>(Attribute.create(clazz, fieldName, fieldType), factory.loadDecoder(fieldType), field, null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <R extends Reader, T, F> DeMember<R, T, F> create(final Attribute<T, F> attribute, final ConvertFactory factory, final Class<F> fieldtype) {
|
||||
return new DeMember<>(attribute, factory.loadDecoder(fieldtype), null, null);
|
||||
public static <R extends Reader, T, F> DeMember<R, T, F> create(final Attribute<T, F> attribute, final ConvertFactory factory, final Class<F> fieldType) {
|
||||
return new DeMember<>(attribute, factory.loadDecoder(fieldType), null, null);
|
||||
}
|
||||
|
||||
public final boolean accepts(String name) {
|
||||
|
||||
@@ -96,6 +96,11 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type getKeyType() {
|
||||
return keyEncoder == null ? null : keyEncoder.getType();
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
|
||||
protected DeMember[] members;
|
||||
|
||||
protected Map<String, DeMember> memberFieldMap;
|
||||
|
||||
protected Map<Integer, DeMember> memberTagMap;
|
||||
|
||||
protected ConvertFactory factory;
|
||||
|
||||
protected volatile boolean inited = false;
|
||||
@@ -92,6 +96,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
final String[] cps = ObjectEncoder.findConstructorProperties(this.creator);
|
||||
try {
|
||||
ConvertColumnEntry ref;
|
||||
ConvertFactory colFactory;
|
||||
RedkaleClassLoader.putReflectionPublicFields(clazz.getName());
|
||||
for (final Field field : clazz.getFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
@@ -99,17 +104,18 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
ref = factory.findRef(clazz, field);
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
ConvertSmallString small = field.getAnnotation(ConvertSmallString.class);
|
||||
colFactory = factory.columnFactory(field.getType(), field.getAnnotationsByType(ConvertCoder.class), false);
|
||||
Decodeable<R, ?> fieldCoder;
|
||||
if (small != null && field.getType() == String.class) {
|
||||
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
|
||||
} else {
|
||||
fieldCoder = factory.findFieldCoder(clazz, field.getName());
|
||||
fieldCoder = colFactory.findFieldCoder(clazz, field.getName());
|
||||
}
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
|
||||
fieldCoder = factory.loadDecoder(t);
|
||||
fieldCoder = colFactory.loadDecoder(t);
|
||||
}
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, field, null, null), fieldCoder, field, null);
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(colFactory, type, clazz, field, null, null), fieldCoder, field, null);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -153,17 +159,22 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
|
||||
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
|
||||
Field maybeField = ConvertFactory.readGetSetField(method);
|
||||
colFactory = factory.columnFactory(method.getParameterTypes()[0], method.getAnnotationsByType(ConvertCoder.class), false);
|
||||
if (maybeField != null && colFactory == factory) {
|
||||
colFactory = factory.columnFactory(maybeField.getType(), maybeField.getAnnotationsByType(ConvertCoder.class), false);
|
||||
}
|
||||
Decodeable<R, ?> fieldCoder;
|
||||
if (small != null && method.getReturnType() == String.class) {
|
||||
if (small != null && method.getParameterTypes()[0] == String.class) {
|
||||
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
|
||||
} else {
|
||||
fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
|
||||
fieldCoder = colFactory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
|
||||
}
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
|
||||
fieldCoder = factory.loadDecoder(t);
|
||||
fieldCoder = colFactory.loadDecoder(t);
|
||||
}
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, null, method), fieldCoder, ConvertFactory.readGetSetField(method), method);
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(colFactory, type, clazz, null, null, method), fieldCoder, maybeField, method);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -217,6 +228,12 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
|
||||
this.members = list.toArray(new DeMember[list.size()]);
|
||||
Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b));
|
||||
this.memberFieldMap = new HashMap<>(this.members.length);
|
||||
this.memberTagMap = new HashMap<>(this.members.length);
|
||||
for (DeMember member : this.members) {
|
||||
this.memberFieldMap.put(member.getAttribute().field(), member);
|
||||
this.memberTagMap.put(member.getTag(), member);
|
||||
}
|
||||
|
||||
if (cps != null) {
|
||||
final String[] fields = cps;
|
||||
@@ -231,6 +248,8 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
}
|
||||
this.creatorConstructorMembers = ms;
|
||||
}
|
||||
|
||||
afterInitDeMember(factory);
|
||||
} catch (Exception ex) {
|
||||
throw new ConvertException(ex);
|
||||
}
|
||||
@@ -242,13 +261,6 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void initForEachDeMember(ConvertFactory factory, DeMember member) {
|
||||
}
|
||||
|
||||
protected void setTag(DeMember member, int tag) {
|
||||
member.tag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2]
|
||||
*
|
||||
@@ -276,37 +288,41 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
throw new ConvertException("[" + typeClass + "] is a interface or abstract class, cannot create it's Creator.");
|
||||
}
|
||||
}
|
||||
|
||||
DeMember[] memberArray = this.members;
|
||||
Map<String, DeMember> fieldMap = this.memberFieldMap;
|
||||
Map<Integer, DeMember> tagMap = this.memberTagMap;
|
||||
if (this.creatorConstructorMembers == null) { //空构造函数
|
||||
final T result = this.creator == null ? null : this.creator.create();
|
||||
boolean first = true;
|
||||
while (hasNext(objin, first)) {
|
||||
DeMember member = objin.readFieldName(members);
|
||||
while (objin.hasNext()) {
|
||||
DeMember member = objin.readFieldName(memberArray, fieldMap, tagMap);
|
||||
objin.readBlank();
|
||||
if (member == null) {
|
||||
objin.skipValue(); //跳过不存在的属性的值
|
||||
} else {
|
||||
readMemberValue(objin, member, result, first);
|
||||
readDeMemberValue(objin, member, result, first);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
objin.readObjectE(typeClass);
|
||||
return result;
|
||||
} else { //带参数的构造函数
|
||||
final DeMember<R, T, ?>[] fields = this.creatorConstructorMembers;
|
||||
final Object[] constructorParams = new Object[fields.length];
|
||||
final DeMember<R, T, ?>[] constructorFields = this.creatorConstructorMembers;
|
||||
final Object[] constructorParams = new Object[constructorFields.length];
|
||||
final Object[][] otherParams = new Object[this.members.length][2];
|
||||
int oc = 0;
|
||||
boolean first = true;
|
||||
while (hasNext(objin, first)) {
|
||||
DeMember member = objin.readFieldName(members);
|
||||
while (objin.hasNext()) {
|
||||
DeMember member = objin.readFieldName(memberArray, fieldMap, tagMap);
|
||||
objin.readBlank();
|
||||
if (member == null) {
|
||||
objin.skipValue(); //跳过不存在的属性的值
|
||||
} else {
|
||||
Object val = readMemberValue(objin, member, first);
|
||||
Object val = readDeMemberValue(objin, member, first);
|
||||
boolean flag = true;
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
if (member == fields[i]) {
|
||||
for (int i = 0; i < constructorFields.length; i++) {
|
||||
if (member == constructorFields[i]) {
|
||||
constructorParams[i] = val;
|
||||
flag = false;
|
||||
break;
|
||||
@@ -327,29 +343,69 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected R objectReader(R in) {
|
||||
return in;
|
||||
//---------------------------------- 可定制方法 ----------------------------------
|
||||
protected void initForEachDeMember(ConvertFactory factory, DeMember member) {
|
||||
}
|
||||
|
||||
protected void afterInitDeMember(ConvertFactory factory) {
|
||||
}
|
||||
|
||||
protected boolean hasNext(R in, boolean first) {
|
||||
return in.hasNext();
|
||||
}
|
||||
|
||||
protected Object readMemberValue(R in, DeMember member, boolean first) {
|
||||
protected R objectReader(R in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
protected Object readDeMemberValue(R in, DeMember member, boolean first) {
|
||||
return member.read(in);
|
||||
}
|
||||
|
||||
protected void readMemberValue(R in, DeMember member, T result, boolean first) {
|
||||
protected void readDeMemberValue(R in, DeMember member, T result, boolean first) {
|
||||
member.read(in, result);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
protected void setTag(DeMember member, int tag) {
|
||||
member.tag = tag;
|
||||
}
|
||||
|
||||
protected void setIndex(DeMember member, int index) {
|
||||
member.index = index;
|
||||
}
|
||||
|
||||
protected void setPosition(DeMember member, int position) {
|
||||
member.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public DeMember[] getMembers() {
|
||||
return Arrays.copyOf(members, members.length);
|
||||
return members;
|
||||
}
|
||||
|
||||
public DeMember getMember(String fieldName) {
|
||||
return memberFieldMap.get(fieldName);
|
||||
}
|
||||
|
||||
public Map<String, DeMember> getMemberFieldMap() {
|
||||
return memberFieldMap;
|
||||
}
|
||||
|
||||
public Map<Integer, DeMember> getMemberTagMap() {
|
||||
return memberTagMap;
|
||||
}
|
||||
|
||||
public DeMember<R, T, ?>[] getConstructorMembers() {
|
||||
return creatorConstructorMembers;
|
||||
}
|
||||
|
||||
public Creator<T> getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
final String[] cps = creator == null ? null : ObjectEncoder.findConstructorProperties(creator);
|
||||
try {
|
||||
ConvertColumnEntry ref;
|
||||
|
||||
ConvertFactory colFactory;
|
||||
RedkaleClassLoader.putReflectionPublicFields(clazz.getName());
|
||||
for (final Field field : clazz.getFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
@@ -81,17 +81,18 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
ref = factory.findRef(clazz, field);
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
ConvertSmallString small = field.getAnnotation(ConvertSmallString.class);
|
||||
colFactory = factory.columnFactory(field.getType(), field.getAnnotationsByType(ConvertCoder.class), true);
|
||||
Encodeable<W, ?> fieldCoder;
|
||||
if (small != null && field.getType() == String.class) {
|
||||
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
|
||||
} else {
|
||||
fieldCoder = factory.findFieldCoder(clazz, field.getName());
|
||||
fieldCoder = colFactory.findFieldCoder(clazz, field.getName());
|
||||
}
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
|
||||
fieldCoder = factory.loadEncoder(t);
|
||||
fieldCoder = colFactory.loadEncoder(t);
|
||||
}
|
||||
EnMember member = new EnMember(createAttribute(factory, type, clazz, field, null, null), fieldCoder, field, null);
|
||||
EnMember member = new EnMember(createAttribute(colFactory, type, clazz, field, null, null), fieldCoder, field, null);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -104,7 +105,9 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
if (method.getName().equals("getClass")) continue;
|
||||
if (method.getReturnType() == void.class) continue;
|
||||
if (method.getParameterCount() != 0) continue;
|
||||
if (!method.getName().startsWith("is") && !method.getName().startsWith("get") && !Utility.isRecordGetter(clazz, method)) continue;
|
||||
if (!(method.getName().startsWith("is") && method.getName().length() > 2)
|
||||
&& !(method.getName().startsWith("get") && method.getName().length() > 3)
|
||||
&& !Utility.isRecordGetter(clazz, method)) continue;
|
||||
if (factory.isConvertDisabled(method)) continue;
|
||||
String convertname = ConvertFactory.readGetSetFieldName(method);
|
||||
if (reversible && (cps == null || !contains(cps, convertname))) {
|
||||
@@ -125,17 +128,22 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
Field maybeField = ConvertFactory.readGetSetField(method);
|
||||
colFactory = factory.columnFactory(method.getReturnType(), method.getAnnotationsByType(ConvertCoder.class), true);
|
||||
if (maybeField != null && colFactory == factory) {
|
||||
colFactory = factory.columnFactory(maybeField.getType(), maybeField.getAnnotationsByType(ConvertCoder.class), true);
|
||||
}
|
||||
Encodeable<W, ?> fieldCoder;
|
||||
if (small != null && method.getReturnType() == String.class) {
|
||||
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
|
||||
} else {
|
||||
fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
|
||||
fieldCoder = colFactory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
|
||||
}
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
|
||||
fieldCoder = factory.loadEncoder(t);
|
||||
fieldCoder = colFactory.loadEncoder(t);
|
||||
}
|
||||
EnMember member = new EnMember(createAttribute(factory, type, clazz, null, method, null), fieldCoder, ConvertFactory.readGetSetField(method), method);
|
||||
EnMember member = new EnMember(createAttribute(colFactory, type, clazz, null, method, null), fieldCoder, maybeField, method);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -197,6 +205,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
this.members = list.toArray(new EnMember[list.size()]);
|
||||
Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b));
|
||||
|
||||
afterInitEnMember(factory);
|
||||
} catch (Exception ex) {
|
||||
throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex);
|
||||
}
|
||||
@@ -208,13 +217,6 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void initForEachEnMember(ConvertFactory factory, EnMember member) {
|
||||
}
|
||||
|
||||
protected void setTag(EnMember member, int tag) {
|
||||
member.tag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, T value) {
|
||||
if (value == null) {
|
||||
@@ -258,10 +260,30 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
objout.writeObjectE(value);
|
||||
}
|
||||
|
||||
//---------------------------------- 可定制方法 ----------------------------------
|
||||
protected void initForEachEnMember(ConvertFactory factory, EnMember member) {
|
||||
}
|
||||
|
||||
protected void afterInitEnMember(ConvertFactory factory) {
|
||||
}
|
||||
|
||||
protected W objectWriter(W out, T value) {
|
||||
return out;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
protected void setTag(EnMember member, int tag) {
|
||||
member.tag = tag;
|
||||
}
|
||||
|
||||
protected void setIndex(EnMember member, int index) {
|
||||
member.index = index;
|
||||
}
|
||||
|
||||
protected void setPosition(EnMember member, int position) {
|
||||
member.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
@@ -272,7 +294,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
}
|
||||
|
||||
public EnMember[] getMembers() {
|
||||
return Arrays.copyOf(members, members.length);
|
||||
return members;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,54 +302,6 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
|
||||
}
|
||||
|
||||
//
|
||||
// static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) {
|
||||
// if (type instanceof Class) { //e.g. String
|
||||
// return type;
|
||||
// } else if (type instanceof ParameterizedType) { //e.g. Map<String, String>
|
||||
// final ParameterizedType pt = (ParameterizedType) type;
|
||||
// Type[] paramTypes = pt.getActualTypeArguments();
|
||||
// final Type[] newTypes = new Type[paramTypes.length];
|
||||
// int count = 0;
|
||||
// for (int i = 0; i < newTypes.length; i++) {
|
||||
// newTypes[i] = makeGenericType(paramTypes[i], virGenericTypes, realGenericTypes);
|
||||
// if (paramTypes[i] == newTypes[i]) count++;
|
||||
// }
|
||||
// if (count == paramTypes.length) return pt;
|
||||
// return new ParameterizedType() {
|
||||
//
|
||||
// @Override
|
||||
// public Type[] getActualTypeArguments() {
|
||||
// return newTypes;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Type getRawType() {
|
||||
// return pt.getRawType();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Type getOwnerType() {
|
||||
// return pt.getOwnerType();
|
||||
// }
|
||||
//
|
||||
// };
|
||||
// }
|
||||
// if (realGenericTypes == null) return type;
|
||||
// if (type instanceof WildcardType) { // e.g. <? extends Serializable>
|
||||
// final WildcardType wt = (WildcardType) type;
|
||||
// for (Type f : wt.getUpperBounds()) {
|
||||
// for (int i = 0; i < virGenericTypes.length; i++) {
|
||||
// if (virGenericTypes[i] == f) return realGenericTypes.length == 0 ? Object.class : realGenericTypes[i];
|
||||
// }
|
||||
// }
|
||||
// } else if (type instanceof TypeVariable) { // e.g. <? extends E>
|
||||
// for (int i = 0; i < virGenericTypes.length; i++) {
|
||||
// if (virGenericTypes[i] == type) return i >= realGenericTypes.length ? Object.class : realGenericTypes[i];
|
||||
// }
|
||||
// }
|
||||
// return type;
|
||||
// }
|
||||
static boolean contains(String[] values, String value) {
|
||||
for (String str : values) {
|
||||
if (str.equals(value)) return true;
|
||||
@@ -371,8 +345,12 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
}
|
||||
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
|
||||
}
|
||||
|
||||
return Attribute.create(realType, clazz, fieldalias, null, field, getter, setter, null);
|
||||
Type subclass = realType;
|
||||
if ((realType instanceof Class) && field != null && getter == null && setter == null) {
|
||||
//修复父类含public field,subclass不传父类会导致java.lang.NoSuchFieldError的bug
|
||||
subclass = field.getDeclaringClass();
|
||||
}
|
||||
return Attribute.create(subclass, clazz, fieldalias, null, field, getter, setter, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 反序列化的数据读取流
|
||||
*
|
||||
@@ -141,11 +143,13 @@ public abstract class Reader {
|
||||
/**
|
||||
* 根据字段读取字段对应的DeMember
|
||||
*
|
||||
* @param members DeMember的全量集合
|
||||
* @param members DeMember的全量集合
|
||||
* @param memberFieldMap DeMember的字段名map
|
||||
* @param memberTagMap DeMember的tag map
|
||||
*
|
||||
* @return 匹配的DeMember
|
||||
*/
|
||||
public abstract DeMember readFieldName(final DeMember[] members);
|
||||
public abstract DeMember readFieldName(final DeMember[] members, Map<String, DeMember> memberFieldMap, Map<Integer, DeMember> memberTagMap);
|
||||
|
||||
/**
|
||||
* 读取一个boolean值
|
||||
|
||||
@@ -97,6 +97,11 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getComponentEncoder() {
|
||||
return componentEncoder;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ public abstract class TextConvert<R extends Reader, W extends Writer> extends Co
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract <T> T convertFrom(final Type type, final String text);
|
||||
|
||||
public abstract String convertTo(final Object value);
|
||||
|
||||
public abstract String convertTo(final Type type, final Object value);
|
||||
|
||||
@@ -58,6 +58,10 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected boolean tiny() {
|
||||
return this.tiny;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BsonFactory skipAllIgnore(final boolean skipIgnore) {
|
||||
this.registerSkipAllIgnore(skipIgnore);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.convert.bson;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import org.redkale.convert.*;
|
||||
import static org.redkale.convert.Reader.SIGN_NULL;
|
||||
import org.redkale.convert.ext.*;
|
||||
@@ -219,19 +220,19 @@ public class BsonReader extends Reader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DeMember readFieldName(final DeMember[] members) {
|
||||
final String exceptedfield = readSmallString();
|
||||
public final DeMember readFieldName(final DeMember[] members, Map<String, DeMember> memberFieldMap, Map<Integer, DeMember> memberTagMap) {
|
||||
final String exceptedField = readSmallString();
|
||||
this.typeval = readByte();
|
||||
final int len = members.length;
|
||||
if (this.fieldIndex >= len) this.fieldIndex = 0;
|
||||
for (int k = this.fieldIndex; k < len; k++) {
|
||||
if (exceptedfield.equals(members[k].getAttribute().field())) {
|
||||
if (exceptedField.equals(members[k].getAttribute().field())) {
|
||||
this.fieldIndex = k;
|
||||
return members[k];
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < this.fieldIndex; k++) {
|
||||
if (exceptedfield.equals(members[k].getAttribute().field())) {
|
||||
if (exceptedField.equals(members[k].getAttribute().field())) {
|
||||
this.fieldIndex = k;
|
||||
return members[k];
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class BsonWriter extends Writer implements ByteTuple {
|
||||
|
||||
protected int count;
|
||||
|
||||
protected boolean tiny;
|
||||
protected boolean tiny = BsonFactory.root().tiny();
|
||||
|
||||
public static ObjectPool<BsonWriter> createPool(int max) {
|
||||
return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.util.Utility;
|
||||
|
||||
/**
|
||||
@@ -41,4 +42,30 @@ public final class BigDecimalSimpledCoder<R extends Reader, W extends Writer> ex
|
||||
return new BigDecimal(Utility.charArray(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* BigDecimal 的JsonSimpledCoder实现
|
||||
*
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public static class BigDecimalJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, BigDecimal> {
|
||||
|
||||
public static final BigDecimalJsonSimpledCoder instance = new BigDecimalJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final W out, final BigDecimal value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
out.writeSmallString(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal convertFrom(R in) {
|
||||
final String str = in.readString();
|
||||
if (str == null) return null;
|
||||
return new BigDecimal(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.Reader;
|
||||
import java.math.BigInteger;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* BigInteger 的SimpledCoder实现
|
||||
@@ -47,24 +48,59 @@ public final class BigIntegerSimpledCoder<R extends Reader, W extends Writer> ex
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public static class BigIntegerJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, BigInteger> {
|
||||
public static class BigIntegerJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, BigInteger> {
|
||||
|
||||
public static final BigIntegerJsonSimpledCoder instance = new BigIntegerJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final Writer out, final BigInteger value) {
|
||||
public void convertTo(final W out, final BigInteger value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
out.writeString(value.toString());
|
||||
out.writeSmallString(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger convertFrom(Reader in) {
|
||||
public BigInteger convertFrom(R in) {
|
||||
final String str = in.readString();
|
||||
if (str == null) return null;
|
||||
return new BigInteger(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BigInteger 的十六进制JsonSimpledCoder实现
|
||||
*
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public static class BigIntegerHexJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, BigInteger> {
|
||||
|
||||
public static final BigIntegerHexJsonSimpledCoder instance = new BigIntegerHexJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final W out, final BigInteger value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
String s = value.toString(16);
|
||||
out.writeSmallString(s.charAt(0) == '-' ? ("-0x" + s.substring(1)) : ("0x" + s));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger convertFrom(R in) {
|
||||
final String str = in.readString();
|
||||
if (str == null) return null;
|
||||
if (str.length() > 2) {
|
||||
if (str.charAt(0) == '0' && (str.charAt(1) == 'x' || str.charAt(1) == 'X')) {
|
||||
return new BigInteger(str.substring(2), 16);
|
||||
} else if (str.charAt(0) == '-' && str.length() > 3 && str.charAt(1) == '0' && (str.charAt(2) == 'x' || str.charAt(2) == 'X')) {
|
||||
return new BigInteger("-" + str.substring(3), 16);
|
||||
}
|
||||
}
|
||||
return new BigInteger(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.convert.ext;
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -50,12 +51,12 @@ public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public static class DLongJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, DLong> {
|
||||
public static class DLongJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, DLong> {
|
||||
|
||||
public static final DLongJsonSimpledCoder instance = new DLongJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final Writer out, final DLong value) {
|
||||
public void convertTo(final W out, final DLong value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
@@ -64,7 +65,7 @@ public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public DLong convertFrom(Reader in) {
|
||||
public DLong convertFrom(R in) {
|
||||
final String str = in.readSmallString();
|
||||
if (str == null) return null;
|
||||
return DLong.create(Utility.hexToBin(str));
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.Reader;
|
||||
import java.net.*;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.util.StringWrapper;
|
||||
|
||||
/**
|
||||
@@ -87,7 +88,7 @@ public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> e
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final static class InetAddressJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetAddress> {
|
||||
public final static class InetAddressJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, InetAddress> {
|
||||
|
||||
public static final InetAddressJsonSimpledCoder instance = new InetAddressJsonSimpledCoder();
|
||||
|
||||
@@ -119,7 +120,7 @@ public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> e
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final static class InetSocketAddressJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetSocketAddress> {
|
||||
public final static class InetSocketAddressJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, InetSocketAddress> {
|
||||
|
||||
public static final InetSocketAddressJsonSimpledCoder instance = new InetSocketAddressJsonSimpledCoder();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.ext;
|
||||
|
||||
import java.time.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* java.time.Instant 的SimpledCoder实现
|
||||
@@ -33,12 +34,12 @@ public class InstantSimpledCoder<R extends Reader, W extends Writer> extends Sim
|
||||
return t == -1 ? null : Instant.ofEpochMilli(t);
|
||||
}
|
||||
|
||||
public final static class InstantJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Instant> {
|
||||
public final static class InstantJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, Instant> {
|
||||
|
||||
public static final InstantJsonSimpledCoder instance = new InstantJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final Writer out, final Instant value) {
|
||||
public void convertTo(final W out, final Instant value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
@@ -47,7 +48,7 @@ public class InstantSimpledCoder<R extends Reader, W extends Writer> extends Sim
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant convertFrom(Reader in) {
|
||||
public Instant convertFrom(R in) {
|
||||
final String str = in.readSmallString();
|
||||
if (str == null) return null;
|
||||
return Instant.parse(str);
|
||||
|
||||
@@ -8,11 +8,14 @@ package org.redkale.convert.ext;
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* int 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -31,4 +34,38 @@ public final class IntSimpledCoder<R extends Reader, W extends Writer> extends S
|
||||
return in.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* int 的十六进制JsonSimpledCoder实现
|
||||
*
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public static class IntHexJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, Integer> {
|
||||
|
||||
public static final IntHexJsonSimpledCoder instance = new IntHexJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final W out, final Integer value) {
|
||||
if (value == null) {
|
||||
out.writeSmallString("0x0");
|
||||
} else {
|
||||
if (value < 0) throw new NumberFormatException("Negative values (" + value + ") are not supported");
|
||||
out.writeSmallString("0x" + Integer.toHexString(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer convertFrom(R in) {
|
||||
final String str = in.readString();
|
||||
if (str == null) return 0;
|
||||
try {
|
||||
if (str.length() > 2 && str.charAt(0) == '0' && (str.charAt(1) == 'x' || str.charAt(1) == 'X')) {
|
||||
return Integer.parseInt(str.substring(2), 16);
|
||||
}
|
||||
return Integer.parseInt(str);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.ext;
|
||||
|
||||
import java.time.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* java.time.LocalDate 的SimpledCoder实现
|
||||
@@ -49,12 +50,12 @@ public final class LocalDateSimpledCoder<R extends Reader, W extends Writer> ext
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final static class LocalDateJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, LocalDate> {
|
||||
public final static class LocalDateJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, LocalDate> {
|
||||
|
||||
public static final LocalDateJsonSimpledCoder instance = new LocalDateJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final Writer out, final LocalDate value) {
|
||||
public void convertTo(final W out, final LocalDate value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
@@ -63,7 +64,7 @@ public final class LocalDateSimpledCoder<R extends Reader, W extends Writer> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate convertFrom(Reader in) {
|
||||
public LocalDate convertFrom(R in) {
|
||||
final String str = in.readSmallString();
|
||||
if (str == null) return null;
|
||||
return LocalDate.parse(str);
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.ext;
|
||||
|
||||
import java.time.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* java.time.LocalDateTime 的SimpledCoder实现
|
||||
@@ -62,12 +63,12 @@ public final class LocalDateTimeSimpledCoder<R extends Reader, W extends Writer>
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final static class LocalDateTimeJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, LocalDateTime> {
|
||||
public final static class LocalDateTimeJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, LocalDateTime> {
|
||||
|
||||
public static final LocalDateTimeJsonSimpledCoder instance = new LocalDateTimeJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final Writer out, final LocalDateTime value) {
|
||||
public void convertTo(final W out, final LocalDateTime value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
@@ -76,7 +77,7 @@ public final class LocalDateTimeSimpledCoder<R extends Reader, W extends Writer>
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime convertFrom(Reader in) {
|
||||
public LocalDateTime convertFrom(R in) {
|
||||
final String str = in.readSmallString();
|
||||
if (str == null) return null;
|
||||
return LocalDateTime.parse(str);
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.ext;
|
||||
|
||||
import java.time.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* java.time.LocalTime 的SimpledCoder实现
|
||||
@@ -47,12 +48,12 @@ public final class LocalTimeSimpledCoder<R extends Reader, W extends Writer> ext
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final static class LocalTimeJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, LocalTime> {
|
||||
public final static class LocalTimeJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, LocalTime> {
|
||||
|
||||
public static final LocalTimeJsonSimpledCoder instance = new LocalTimeJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final Writer out, final LocalTime value) {
|
||||
public void convertTo(final W out, final LocalTime value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
@@ -61,7 +62,7 @@ public final class LocalTimeSimpledCoder<R extends Reader, W extends Writer> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime convertFrom(Reader in) {
|
||||
public LocalTime convertFrom(R in) {
|
||||
final String str = in.readSmallString();
|
||||
if (str == null) return null;
|
||||
return LocalTime.parse(str);
|
||||
|
||||
@@ -3,17 +3,19 @@
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* long 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -32,4 +34,38 @@ public final class LongSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
return in.readLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* long 的十六进制JsonSimpledCoder实现
|
||||
*
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public static class LongHexJsonSimpledCoder<R extends JsonReader, W extends JsonWriter> extends SimpledCoder<R, W, Long> {
|
||||
|
||||
public static final LongHexJsonSimpledCoder instance = new LongHexJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(final W out, final Long value) {
|
||||
if (value == null) {
|
||||
out.writeSmallString("0x0");
|
||||
} else {
|
||||
if (value < 0) throw new NumberFormatException("Negative values (" + value + ") are not supported");
|
||||
out.writeSmallString("0x" + Long.toHexString(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long convertFrom(R in) {
|
||||
final String str = in.readString();
|
||||
if (str == null) return 0L;
|
||||
try {
|
||||
if (str.length() > 2 && str.charAt(0) == '0' && (str.charAt(1) == 'x' || str.charAt(1) == 'X')) {
|
||||
return Long.parseLong(str.substring(2), 16);
|
||||
}
|
||||
return Long.parseLong(str);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,21 +98,6 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取下一个有效字符
|
||||
*
|
||||
* @return 有效字符
|
||||
*/
|
||||
@Override
|
||||
protected final char nextGoodChar() {
|
||||
char c = nextChar();
|
||||
if (c > ' ' || c == 0) return c; // 0 表示buffer结尾了
|
||||
for (;;) {
|
||||
c = nextChar();
|
||||
if (c > ' ' || c == 0) return c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退最后读取的字符
|
||||
*
|
||||
@@ -130,7 +115,8 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
*/
|
||||
@Override
|
||||
public final String readObjectB(final Class clazz) {
|
||||
char ch = nextGoodChar();
|
||||
this.fieldIndex = 0; //必须要重置为0
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == '{') return "";
|
||||
if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return null;
|
||||
if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return null;
|
||||
@@ -155,7 +141,7 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
*/
|
||||
@Override
|
||||
public final int readArrayB(DeMember member, byte[] typevals, Decodeable decoder) {
|
||||
char ch = nextGoodChar();
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == '[' || ch == '{') return SIGN_NOLENGTH;
|
||||
if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return SIGN_NULL;
|
||||
if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return SIGN_NULL;
|
||||
@@ -174,7 +160,7 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
*/
|
||||
@Override
|
||||
public final void readBlank() {
|
||||
char ch = nextGoodChar();
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == ':') return;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ch);
|
||||
@@ -186,23 +172,6 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
throw new ConvertException("expected a ':' but '" + ch + "'(position = " + position + ") in (" + sb + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
|
||||
*
|
||||
* @param startPosition 起始位置
|
||||
* @param contentLength 内容大小, 不确定的传-1
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext(int startPosition, int contentLength) {
|
||||
char ch = nextGoodChar();
|
||||
if (ch == ',') return true;
|
||||
if (ch == '}' || ch == ']' || ch == 0) return false;
|
||||
backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取小字符串
|
||||
*
|
||||
@@ -210,7 +179,7 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
*/
|
||||
@Override
|
||||
public final String readSmallString() {
|
||||
char ch = nextGoodChar();
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == 0) return null;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (ch == '"' || ch == '\'') {
|
||||
@@ -300,80 +269,6 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个int值
|
||||
*
|
||||
* @return int值
|
||||
*/
|
||||
@Override
|
||||
public final int readInt() {
|
||||
char firstchar = nextGoodChar();
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
quote = true;
|
||||
firstchar = nextGoodChar();
|
||||
if (firstchar == '"' || firstchar == '\'') return 0;
|
||||
}
|
||||
int value = 0;
|
||||
final boolean negative = firstchar == '-';
|
||||
if (!negative) {
|
||||
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")");
|
||||
value = firstchar - '0';
|
||||
}
|
||||
for (;;) {
|
||||
char ch = nextChar();
|
||||
if (ch == 0) break;
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
value = (value << 3) + (value << 1) + (ch - '0');
|
||||
} else if (ch == '"' || ch == '\'') {
|
||||
} else if (quote && ch <= ' ') {
|
||||
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
|
||||
backChar(ch);
|
||||
break;
|
||||
} else {
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
}
|
||||
}
|
||||
return negative ? -value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个long值
|
||||
*
|
||||
* @return long值
|
||||
*/
|
||||
@Override
|
||||
public final long readLong() {
|
||||
char firstchar = nextGoodChar();
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
quote = true;
|
||||
firstchar = nextGoodChar();
|
||||
if (firstchar == '"' || firstchar == '\'') return 0L;
|
||||
}
|
||||
long value = 0;
|
||||
final boolean negative = firstchar == '-';
|
||||
if (!negative) {
|
||||
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")");
|
||||
value = firstchar - '0';
|
||||
}
|
||||
for (;;) {
|
||||
char ch = nextChar();
|
||||
if (ch == 0) break;
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
value = (value << 3) + (value << 1) + (ch - '0');
|
||||
} else if (ch == '"' || ch == '\'') {
|
||||
} else if (quote && ch <= ' ') {
|
||||
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
|
||||
backChar(ch);
|
||||
break;
|
||||
} else {
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
}
|
||||
}
|
||||
return negative ? -value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取字符串, 必须是"或者'包围的字符串值
|
||||
*
|
||||
|
||||
@@ -263,7 +263,7 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String
|
||||
*
|
||||
* @param quote 是否写入双引号
|
||||
* @param value String值
|
||||
@@ -622,6 +622,11 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
|
||||
@Override
|
||||
public void writeString(String value) {
|
||||
writeString(true, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(final boolean quote, String value) {
|
||||
if (value == null) {
|
||||
writeNull();
|
||||
return;
|
||||
@@ -654,7 +659,7 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
expandsize = expand(byteLength);
|
||||
if (expandsize == 0) { // 只需要一个buffer
|
||||
final ByteBuffer buffer = this.buffers[index];
|
||||
buffer.put((byte) '"');
|
||||
if (quote) buffer.put((byte) '"');
|
||||
for (int i = 0; i < chs.length; i++) {
|
||||
char c = chs[i];
|
||||
switch (c) {
|
||||
@@ -689,7 +694,7 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer.put((byte) '"');
|
||||
if (quote) buffer.put((byte) '"');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -711,7 +716,7 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
}
|
||||
}
|
||||
char[] cs = Utility.charArray(sb);
|
||||
writeTo(expandsize, true, cs, 0, sb.length());
|
||||
writeTo(expandsize, quote, cs, 0, sb.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -137,7 +137,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String
|
||||
*
|
||||
* @param quote 是否加双引号
|
||||
* @param value 非null且不含需要转义的字符的String值
|
||||
@@ -363,10 +363,10 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
private void writeEscapeLatinString(byte[] value) {
|
||||
private void writeEscapeLatinString(final boolean quote, byte[] value) {
|
||||
byte[] bytes = expand(value.length * 2 + 2);
|
||||
int curr = count;
|
||||
bytes[curr++] = '"';
|
||||
if (quote) bytes[curr++] = '"';
|
||||
for (byte b : value) {
|
||||
if (b == '"') {
|
||||
bytes[curr++] = '\\';
|
||||
@@ -391,23 +391,28 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
bytes[curr++] = b;
|
||||
}
|
||||
}
|
||||
bytes[curr++] = '"';
|
||||
if (quote) bytes[curr++] = '"';
|
||||
count = curr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(String value) {
|
||||
writeString(true, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(final boolean quote, String value) {
|
||||
if (value == null) {
|
||||
writeNull();
|
||||
return;
|
||||
}
|
||||
if (Utility.isLatin1(value)) {
|
||||
writeEscapeLatinString(Utility.latin1ByteArray(value));
|
||||
writeEscapeLatinString(quote, Utility.latin1ByteArray(value));
|
||||
return;
|
||||
}
|
||||
byte[] bytes = expand(value.length() * 4 + 2);
|
||||
int curr = count;
|
||||
bytes[curr++] = '"';
|
||||
if (quote) bytes[curr++] = '"';
|
||||
int len = value.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
char ch = value.charAt(i);
|
||||
@@ -452,7 +457,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
break;
|
||||
}
|
||||
}
|
||||
bytes[curr++] = '"';
|
||||
if (quote) bytes[curr++] = '"';
|
||||
count = curr;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public class JsonCharsWriter extends JsonWriter {
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String
|
||||
*
|
||||
* @param quote 是否加双引号
|
||||
* @param value 非null且不含需要转义的字符的String值
|
||||
@@ -308,12 +308,17 @@ public class JsonCharsWriter extends JsonWriter {
|
||||
|
||||
@Override
|
||||
public void writeString(String value) {
|
||||
writeString(true, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(final boolean quote, String value) {
|
||||
if (value == null) {
|
||||
writeNull();
|
||||
return;
|
||||
}
|
||||
expand(value.length() * 2 + 2);
|
||||
content[count++] = '"';
|
||||
if (quote) content[count++] = '"';
|
||||
for (char ch : Utility.charArray(value)) {
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
@@ -341,7 +346,7 @@ public class JsonCharsWriter extends JsonWriter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
content[count++] = '"';
|
||||
if (quote) content[count++] = '"';
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,9 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() {
|
||||
}.getType();
|
||||
|
||||
public static final Type TYPE_LIST_STRING = new TypeToken<java.util.List<String>>() {
|
||||
}.getType();
|
||||
|
||||
public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
|
||||
}.getType();
|
||||
|
||||
@@ -100,6 +103,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
return convertFrom(type, new String(bytes, offset, length, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convertFrom(final Type type, final String text) {
|
||||
if (text == null) return null;
|
||||
return convertFrom(type, Utility.charArray(text));
|
||||
@@ -211,6 +215,24 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
return (V) new AnyDecoder(factory).convertFrom(reader);
|
||||
}
|
||||
|
||||
//json数据的数组长度必须和types个数相同
|
||||
public Object[] convertFrom(final Type[] types, final String text) {
|
||||
if (text == null) return null;
|
||||
return new JsonMultiArrayDecoder(getFactory(), types).convertFrom(new JsonReader(text));
|
||||
}
|
||||
|
||||
//json数据的数组长度必须和types个数相同
|
||||
public Object[] convertFrom(final Type[] types, final byte[] bytes) {
|
||||
if (bytes == null) return null;
|
||||
return convertFrom(types, new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
//json数据的数组长度必须和types个数相同
|
||||
public Object[] convertFrom(final Type[] types, final byte[] bytes, final int offset, final int length) {
|
||||
if (bytes == null) return null;
|
||||
return convertFrom(types, new String(bytes, offset, length, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
//------------------------------ convertTo -----------------------------------------------------------
|
||||
@Override
|
||||
public String convertTo(final Object value) {
|
||||
|
||||
@@ -129,7 +129,8 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
|
||||
if (method.isSynthetic()) continue;
|
||||
if (method.getName().length() < 3) continue;
|
||||
if (method.getName().equals("getClass")) continue;
|
||||
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
|
||||
if (!(method.getName().startsWith("is") && method.getName().length() > 2)
|
||||
&& !(method.getName().startsWith("get") && method.getName().length() > 3)) continue;
|
||||
if (factory.isConvertDisabled(method)) continue;
|
||||
if (method.getParameterTypes().length != 0) continue;
|
||||
if (method.getReturnType() == void.class) continue;
|
||||
@@ -195,7 +196,9 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
|
||||
Method method = (Method) element;
|
||||
if (method == null) return null;
|
||||
String fname = method.getName();
|
||||
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname;
|
||||
if (!(fname.startsWith("is") && fname.length() > 2)
|
||||
&& !(fname.startsWith("get") && fname.length() > 3)
|
||||
&& !(fname.startsWith("set") && fname.length() > 3)) return fname;
|
||||
fname = fname.substring(fname.startsWith("is") ? 2 : 3);
|
||||
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
|
||||
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
|
||||
@@ -206,6 +209,10 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
|
||||
}
|
||||
|
||||
protected static JsonDynEncoder generateDyncEncoder(final JsonFactory factory, final Class clazz, final List<AccessibleObject> members) {
|
||||
final ObjectEncoder selfObjEncoder = factory.createObjectEncoder(clazz);
|
||||
selfObjEncoder.init(factory);
|
||||
if (selfObjEncoder.getMembers().length != members.size()) return null; //存在ignore等定制配置
|
||||
|
||||
final String supDynName = JsonDynEncoder.class.getName().replace('.', '/');
|
||||
final String valtypeName = clazz.getName().replace('.', '/');
|
||||
final String writerName = JsonWriter.class.getName().replace('.', '/');
|
||||
@@ -220,9 +227,11 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
|
||||
final String valtypeDesc = org.redkale.asm.Type.getDescriptor(clazz);
|
||||
|
||||
Map<String, AccessibleObject> mixedNames0 = null;
|
||||
StringBuilder memberb = new StringBuilder();
|
||||
for (AccessibleObject element : members) {
|
||||
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
|
||||
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
|
||||
memberb.append(fieldname).append(',');
|
||||
final Class fieldtype = readGetSetFieldType(element);
|
||||
if (fieldtype != String.class && !fieldtype.isPrimitive()) {
|
||||
if (mixedNames0 == null) mixedNames0 = new HashMap<>();
|
||||
@@ -231,9 +240,8 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
|
||||
}
|
||||
final Map<String, AccessibleObject> mixedNames = mixedNames0;
|
||||
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
final String newDynName = "org/redkaledyn/json/_Dyn" + JsonDynEncoder.class.getSimpleName() + "__" + clazz.getName().replace('.', '_').replace('$', '_');
|
||||
final ObjectEncoder selfObjEncoder = factory.createObjectEncoder(clazz);
|
||||
selfObjEncoder.init(factory);
|
||||
final String newDynName = "org/redkaledyn/json/_Dyn" + JsonDynEncoder.class.getSimpleName()
|
||||
+ "__" + clazz.getName().replace('.', '_').replace('$', '_') + "_" + factory.tiny() + "_" + Utility.md5Hex(memberb.toString()); //tiny必须要加上, 同一个类会有多个字段定制Convert
|
||||
try {
|
||||
Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.'));
|
||||
Class newClazz = clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz;
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.redkale.convert.json;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.*;
|
||||
import java.math.BigInteger;
|
||||
import java.math.*;
|
||||
import java.net.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.ext.*;
|
||||
@@ -24,7 +24,7 @@ import org.redkale.util.*;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
|
||||
private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("redkaleconvert.json.tiny", "redkale.convert.tiny", true));
|
||||
private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", true));
|
||||
|
||||
static {
|
||||
instance.register(Serializable.class, instance.loadEncoder(Object.class));
|
||||
@@ -40,6 +40,7 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
|
||||
this.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
|
||||
this.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
|
||||
this.register(BigDecimal.class, BigDecimalSimpledCoder.BigDecimalJsonSimpledCoder.instance);
|
||||
this.register(java.time.Instant.class, InstantSimpledCoder.InstantJsonSimpledCoder.instance);
|
||||
this.register(java.time.LocalDate.class, LocalDateSimpledCoder.LocalDateJsonSimpledCoder.instance);
|
||||
this.register(java.time.LocalTime.class, LocalTimeSimpledCoder.LocalTimeJsonSimpledCoder.instance);
|
||||
@@ -64,7 +65,7 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
}
|
||||
|
||||
public static JsonFactory create() {
|
||||
return new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "convert.tiny", true));
|
||||
return new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,6 +78,11 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
return super.createObjectEncoder(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <E> Decodeable<JsonReader, E> createMultiImplDecoder(Class[] types) {
|
||||
return new JsonMultiImplDecoder(this, types);
|
||||
}
|
||||
|
||||
protected boolean tiny() {
|
||||
return this.tiny;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.convert.json;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* 数组数据中包含不同Type的反序列化解析器 <br>
|
||||
* 如: ['aaa',{'name':'hahah'}], 需要两个Type来反序列化(String, Map<String, String>) <br>
|
||||
* <b>注意: type的个数必须大于或等于结果数组元素个数, 此解析器对象不会被缓存,每次都会创建新实例</b>
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class JsonMultiArrayDecoder implements Decodeable<JsonReader, Object[]> {
|
||||
|
||||
protected final JsonFactory factory;
|
||||
|
||||
protected final Type[] types;
|
||||
|
||||
protected final Decodeable[] decoders;
|
||||
|
||||
public JsonMultiArrayDecoder(final JsonFactory factory, final Type[] types) {
|
||||
this.factory = factory;
|
||||
this.types = types;
|
||||
this.decoders = new Decodeable[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
this.decoders[i] = factory.loadDecoder(types[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] convertFrom(JsonReader in) {
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public Object[] convertFrom(JsonReader in, DeMember member) {
|
||||
int len = in.readArrayB(member, null, null);
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
//len must be Reader.SIGN_NOLENGTH
|
||||
final List<Object> result = new ArrayList();
|
||||
int startPosition = in.position();
|
||||
int index = -1;
|
||||
final Decodeable[] coders = this.decoders;
|
||||
while (in.hasNext(startPosition, -1)) {
|
||||
result.add(coders[++index % coders.length].convertFrom(in));
|
||||
}
|
||||
in.readArrayE();
|
||||
return result.toArray(new Object[result.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Object[].class;
|
||||
}
|
||||
|
||||
}
|
||||
181
src/main/java/org/redkale/convert/json/JsonMultiImplDecoder.java
Normal file
181
src/main/java/org/redkale/convert/json/JsonMultiImplDecoder.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.convert.json;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 抽象或接口类存在多种实现类的反序列化解析器 <br>
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 泛型
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class JsonMultiImplDecoder<T> implements Decodeable<JsonReader, T> {
|
||||
|
||||
protected final JsonFactory factory;
|
||||
|
||||
protected final Class[] types;
|
||||
|
||||
protected final ObjectDecoder[] decoders;
|
||||
|
||||
protected final int maxMemberCount;
|
||||
|
||||
protected final ObjectDecoder firstDecoder;
|
||||
|
||||
protected final Map<String, ObjectDecoder> repeatFieldToDecoders = new HashMap<>();
|
||||
|
||||
protected final Map<String, ObjectDecoder> uniqueFieldToDecoders = new HashMap<>();
|
||||
|
||||
public JsonMultiImplDecoder(final JsonFactory factory, final Class[] types) {
|
||||
this.factory = factory;
|
||||
this.types = types;
|
||||
this.decoders = new ObjectDecoder[types.length];
|
||||
int max = 0;
|
||||
Set<String>[] fields = new Set[types.length];
|
||||
Set<String>[] movsets = new Set[types.length];
|
||||
Map<String, Attribute> fieldTypes = new HashMap<>();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
movsets[i] = new HashSet();
|
||||
fields[i] = new HashSet<>();
|
||||
ObjectDecoder decoder = (ObjectDecoder) factory.loadDecoder(types[i]);
|
||||
if (decoder.getMembers().length > max) {
|
||||
max = decoder.getMembers().length;
|
||||
}
|
||||
for (DeMember member : decoder.getMembers()) {
|
||||
String name = member.getAttribute().field();
|
||||
this.repeatFieldToDecoders.put(name, decoder);
|
||||
fields[i].add(name);
|
||||
Attribute t = fieldTypes.get(name);
|
||||
if (t == null) {
|
||||
fieldTypes.put(name, member.getAttribute());
|
||||
} else if (!member.getAttribute().genericType().equals(t.genericType())) {
|
||||
throw new RuntimeException("Field(" + name + ")'s Type is not same in " + member.getAttribute().declaringClass() + " and " + t.declaringClass());
|
||||
}
|
||||
}
|
||||
this.decoders[i] = decoder;
|
||||
}
|
||||
this.maxMemberCount = max;
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Set<String> removes = movsets[i];
|
||||
for (String s : fields[i]) {
|
||||
boolean repeat = false;
|
||||
for (int j = 0; j < fields.length; j++) {
|
||||
if (j == i) continue;
|
||||
if (fields[j].contains(s)) {
|
||||
repeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (repeat) removes.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
int min = max + 1;
|
||||
ObjectDecoder first = null; //字段最少的类作为默认反解析器
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Set<String> fieldSet = fields[i];
|
||||
for (String s : movsets[i]) {
|
||||
fieldSet.remove(s); //移除重复的字段
|
||||
}
|
||||
if (fieldSet.size() < min) {
|
||||
first = this.decoders[i];
|
||||
min = fieldSet.size();
|
||||
}
|
||||
for (String s : fieldSet) {
|
||||
this.uniqueFieldToDecoders.put(s, this.decoders[i]);
|
||||
this.repeatFieldToDecoders.remove(s);
|
||||
}
|
||||
}
|
||||
this.firstDecoder = first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convertFrom(JsonReader in) {
|
||||
final String clazz = in.readObjectB(null);
|
||||
if (clazz == null) return null;
|
||||
ObjectDecoder decoder = this.firstDecoder;
|
||||
Map<String, ObjectDecoder> uniques = this.uniqueFieldToDecoders;
|
||||
Map<String, ObjectDecoder> repeats = this.repeatFieldToDecoders;
|
||||
int index = -1;
|
||||
boolean finaled = false;
|
||||
final Object[][] params = new Object[this.maxMemberCount][2];
|
||||
while (in.hasNext()) {
|
||||
String fieldName = in.readFieldName();
|
||||
DeMember member = decoder.getMember(fieldName);
|
||||
//new Set[]{Utility.ofSet("1", "2", "3"), Utility.ofSet("2", "3"), Utility.ofSet("4", "2", "3"), Utility.ofSet("6", "7", "8"), Utility.ofSet("6", "9")};
|
||||
if (member == null && !finaled) {
|
||||
ObjectDecoder de = uniques.get(fieldName);
|
||||
if (de == null) {
|
||||
de = repeats.get(fieldName);
|
||||
if (de != null) {
|
||||
decoder = de;
|
||||
member = de.getMember(fieldName);
|
||||
for (int i = 0; i <= index; i++) { //迁移params中的DeMember.Attribute
|
||||
if (params[i] != null) {
|
||||
DeMember dm = de.getMember(((Attribute) params[i][0]).field());
|
||||
params[i][0] = dm == null ? null : dm.getAttribute();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
finaled = true;
|
||||
decoder = de;
|
||||
member = de.getMember(fieldName);
|
||||
for (int i = 0; i <= index; i++) { //迁移params中的DeMember.Attribute
|
||||
if (params[i] != null) {
|
||||
DeMember dm = de.getMember(((Attribute) params[i][0]).field());
|
||||
params[i][0] = dm == null ? null : dm.getAttribute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
in.readBlank();
|
||||
if (member == null) {
|
||||
in.skipValue(); //跳过不存在的属性的值
|
||||
} else {
|
||||
params[++index] = new Object[]{member.getAttribute(), member.read(in)};
|
||||
}
|
||||
}
|
||||
in.readObjectE(null);
|
||||
if (decoder.getConstructorMembers() == null) { //空构造函数
|
||||
T result = (T) decoder.getCreator().create();
|
||||
for (int i = 0; i <= index; i++) {
|
||||
((Attribute) params[i][0]).set(result, params[i][1]);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
final DeMember[] constructorFields = decoder.getConstructorMembers();
|
||||
final Object[] constructorParams = new Object[constructorFields.length];
|
||||
for (int i = 0; i < constructorFields.length; i++) {
|
||||
for (int j = 0; j < params.length; j++) {
|
||||
if (params[j] != null && params[j][0] != null
|
||||
&& constructorFields[i].getAttribute().field().equals(((Attribute) params[j][0]).field())) {
|
||||
constructorParams[i] = params[j][1];
|
||||
params[j] = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
final T result = (T) decoder.getCreator().create(constructorParams);
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (params[i] != null && params[i][0] != null) {
|
||||
((Attribute) params[i][0]).set(result, params[i][1]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Object.class;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.convert.json;
|
||||
|
||||
import java.util.Map;
|
||||
import org.redkale.convert.*;
|
||||
import static org.redkale.convert.Reader.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -76,7 +77,7 @@ public class JsonReader extends Reader {
|
||||
public final void seek(String key) {
|
||||
if (key == null || key.length() < 1) return;
|
||||
final String[] keys = key.split("\\.");
|
||||
nextGoodChar(); //读掉 { [
|
||||
nextGoodChar(true); //读掉 { [
|
||||
for (String key1 : keys) {
|
||||
while (this.hasNext()) {
|
||||
String field = this.readSmallString();
|
||||
@@ -93,7 +94,7 @@ public class JsonReader extends Reader {
|
||||
*/
|
||||
@Override
|
||||
public final void skipValue() {
|
||||
final char ch = nextGoodChar();
|
||||
final char ch = nextGoodChar(true);
|
||||
switch (ch) {
|
||||
case '"':
|
||||
case '\'':
|
||||
@@ -131,20 +132,46 @@ public class JsonReader extends Reader {
|
||||
* @return 空白字符或有效字符
|
||||
*/
|
||||
protected char nextChar() {
|
||||
return this.text[++this.position];
|
||||
int p = ++this.position;
|
||||
if (p >= text.length) return 0;
|
||||
return this.text[p];
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过空白字符, 返回一个非空白字符
|
||||
* 跳过空白字符、单行或多行注释, 返回一个非空白字符
|
||||
*
|
||||
* @param allowComment 是否容许含注释
|
||||
*
|
||||
* @return 有效字符
|
||||
*/
|
||||
protected char nextGoodChar() {
|
||||
char c = nextChar();
|
||||
if (c > ' ') return c;
|
||||
protected char nextGoodChar(boolean allowComment) {
|
||||
char c;
|
||||
for (;;) {
|
||||
c = nextChar();
|
||||
if (c > ' ') return c;
|
||||
if (c == 0) return c;// 0 表示buffer结尾了
|
||||
if (c > ' ') {
|
||||
if (allowComment && c == '/') { //支持单行和多行注释
|
||||
char n = nextChar();
|
||||
if (n == '/') {
|
||||
for (;;) {
|
||||
if (nextChar() == '\n') break;
|
||||
}
|
||||
return nextGoodChar(allowComment);
|
||||
} else if (n == '*') {
|
||||
char nc;
|
||||
char lc = 0;
|
||||
for (;;) {
|
||||
nc = nextChar();
|
||||
if (nc == '/' && lc == '*') break;
|
||||
lc = nc;
|
||||
}
|
||||
return nextGoodChar(allowComment);
|
||||
} else {
|
||||
throw new ConvertException("illegal escape(" + n + ") (position = " + this.position + ") in '" + new String(text) + "'");
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,9 +184,31 @@ public class JsonReader extends Reader {
|
||||
this.position--;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否{开头的对象字符
|
||||
*
|
||||
* @return 是否对象字符
|
||||
*/
|
||||
public boolean isNextObject() {
|
||||
char ch = nextGoodChar(true);
|
||||
backChar(ch);
|
||||
return ch == '{';
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否[开头的数组字符
|
||||
*
|
||||
* @return 是否数组字符
|
||||
*/
|
||||
public boolean isNextArray() {
|
||||
char ch = nextGoodChar(true);
|
||||
backChar(ch);
|
||||
return ch == '[';
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ValueType readType() {
|
||||
char ch = nextGoodChar();
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == '{') {
|
||||
backChar(ch);
|
||||
return ValueType.MAP;
|
||||
@@ -183,15 +232,8 @@ public class JsonReader extends Reader {
|
||||
public String readObjectB(final Class clazz) {
|
||||
this.fieldIndex = 0; //必须要重置为0
|
||||
if (this.text.length == 0) return null;
|
||||
char ch = this.text[++this.position];
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == '{') return "";
|
||||
if (ch <= ' ') {
|
||||
for (;;) {
|
||||
ch = this.text[++this.position];
|
||||
if (ch > ' ') break;
|
||||
}
|
||||
if (ch == '{') return "";
|
||||
}
|
||||
if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return null;
|
||||
if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return null;
|
||||
throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")");
|
||||
@@ -232,17 +274,9 @@ public class JsonReader extends Reader {
|
||||
@Override
|
||||
public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) {
|
||||
if (this.text.length == 0) return SIGN_NULL;
|
||||
char ch = this.text[++this.position];
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == '[') return SIGN_NOLENGTH;
|
||||
if (ch == '{') return SIGN_NOLENGTH;
|
||||
if (ch <= ' ') {
|
||||
for (;;) {
|
||||
ch = this.text[++this.position];
|
||||
if (ch > ' ') break;
|
||||
}
|
||||
if (ch == '[') return SIGN_NOLENGTH;
|
||||
if (ch == '{') return SIGN_NOLENGTH;
|
||||
}
|
||||
if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL;
|
||||
if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL;
|
||||
throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")");
|
||||
@@ -257,15 +291,8 @@ public class JsonReader extends Reader {
|
||||
*/
|
||||
@Override
|
||||
public void readBlank() {
|
||||
char ch = this.text[++this.position];
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == ':') return;
|
||||
if (ch <= ' ') {
|
||||
for (;;) {
|
||||
ch = this.text[++this.position];
|
||||
if (ch > ' ') break;
|
||||
}
|
||||
if (ch == ':') return;
|
||||
}
|
||||
throw new ConvertException("'" + new String(text) + "'expected a ':' but '" + ch + "'(position = " + position + ") in (" + new String(this.text) + ")");
|
||||
}
|
||||
|
||||
@@ -289,17 +316,14 @@ public class JsonReader extends Reader {
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext(int startPosition, int contentLength) {
|
||||
char ch = this.text[++this.position];
|
||||
if (ch == ',') return true;
|
||||
if (ch == '}' || ch == ']') return false;
|
||||
if (ch <= ' ') {
|
||||
for (;;) {
|
||||
ch = this.text[++this.position];
|
||||
if (ch > ' ') break;
|
||||
}
|
||||
if (ch == ',') return true;
|
||||
if (ch == '}' || ch == ']') return false;
|
||||
char ch = nextGoodChar(true);
|
||||
if (ch == ',') {
|
||||
char nt = nextGoodChar(true);
|
||||
if (nt == '}' || nt == ']') return false;
|
||||
this.position--;
|
||||
return true;
|
||||
}
|
||||
if (ch == '}' || ch == ']') return false;
|
||||
this.position--; // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取
|
||||
return true;
|
||||
}
|
||||
@@ -313,15 +337,9 @@ public class JsonReader extends Reader {
|
||||
public String readSmallString() {
|
||||
final int eof = this.limit;
|
||||
if (this.position == eof) return null;
|
||||
char ch = nextGoodChar(true); //需要跳过注释
|
||||
final char[] text0 = this.text;
|
||||
int currpos = this.position;
|
||||
char ch = text0[++currpos];
|
||||
if (ch <= ' ') {
|
||||
for (;;) {
|
||||
ch = text0[++currpos];
|
||||
if (ch > ' ') break;
|
||||
}
|
||||
}
|
||||
if (ch == '"' || ch == '\'') {
|
||||
final char quote = ch;
|
||||
final int start = currpos + 1;
|
||||
@@ -363,55 +381,84 @@ public class JsonReader extends Reader {
|
||||
*/
|
||||
@Override
|
||||
public int readInt() {
|
||||
final char[] text0 = this.text;
|
||||
final int eof = this.limit;
|
||||
int currpos = this.position;
|
||||
char firstchar = text0[++currpos];
|
||||
if (firstchar <= ' ') {
|
||||
for (;;) {
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
char firstchar = nextGoodChar(true);
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
quote = true;
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar <= ' ') {
|
||||
for (;;) {
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
this.position = currpos;
|
||||
return 0;
|
||||
}
|
||||
firstchar = nextGoodChar(false);
|
||||
if (firstchar == '"' || firstchar == '\'') return 0;
|
||||
}
|
||||
int value = 0;
|
||||
final boolean negative = firstchar == '-';
|
||||
if (!negative) {
|
||||
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")");
|
||||
if (firstchar == '+') firstchar = nextChar(); //兼容+开头的
|
||||
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")");
|
||||
value = firstchar - '0';
|
||||
}
|
||||
if (firstchar == 'N') {
|
||||
if (negative) throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")");
|
||||
char c = nextChar();
|
||||
if (c != 'a') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'N') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
if (quote) {
|
||||
c = nextChar();
|
||||
if (c != '"' && c != '\'') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
}
|
||||
return 0; //NaN 返回0;
|
||||
} else if (firstchar == 'I') { //Infinity
|
||||
char c = nextChar();
|
||||
if (c != 'n') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'f') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'i') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'n') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'i') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 't') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'y') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
if (quote) {
|
||||
c = nextChar();
|
||||
if (c != '"' && c != '\'') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
}
|
||||
return negative ? Integer.MIN_VALUE : Integer.MAX_VALUE;
|
||||
}
|
||||
boolean hex = false;
|
||||
boolean dot = false;
|
||||
for (;;) {
|
||||
if (currpos == eof) break;
|
||||
char ch = text0[++currpos];
|
||||
int val = digits[ch];
|
||||
if (quote && val == -3) continue;
|
||||
if (val <= -3) break;
|
||||
if (dot) continue;
|
||||
if (val == -1) {
|
||||
if (ch == '.') {
|
||||
dot = true;
|
||||
continue;
|
||||
}
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
|
||||
char ch = nextChar();
|
||||
if (ch == 0) break;
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (dot) continue;
|
||||
value = (hex ? (value << 4) : ((value << 3) + (value << 1))) + digits[ch];
|
||||
} else if (ch == '"' || ch == '\'') {
|
||||
if (quote) break;
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
} else if (ch == 'x' || ch == 'X') {
|
||||
if (value != 0) throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
hex = true;
|
||||
} else if (ch >= 'a' && ch <= 'f') {
|
||||
if (!hex) throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
if (dot) continue;
|
||||
value = (value << 4) + digits[ch];
|
||||
} else if (ch >= 'A' && ch <= 'F') {
|
||||
if (!hex) throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
if (dot) continue;
|
||||
value = (value << 4) + digits[ch];
|
||||
} else if (quote && ch <= ' ') {
|
||||
} else if (ch == '.') {
|
||||
dot = true;
|
||||
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
|
||||
backChar(ch);
|
||||
break;
|
||||
} else {
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
}
|
||||
if (val != -2) value = value * 10 + val;
|
||||
}
|
||||
this.position = currpos - 1;
|
||||
return negative ? -value : value;
|
||||
}
|
||||
|
||||
@@ -422,72 +469,105 @@ public class JsonReader extends Reader {
|
||||
*/
|
||||
@Override
|
||||
public long readLong() {
|
||||
final char[] text0 = this.text;
|
||||
final int eof = this.limit;
|
||||
int currpos = this.position;
|
||||
char firstchar = text0[++currpos];
|
||||
if (firstchar <= ' ') {
|
||||
for (;;) {
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
char firstchar = nextGoodChar(true);
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
quote = true;
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar <= ' ') {
|
||||
for (;;) {
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
this.position = currpos;
|
||||
return 0L;
|
||||
}
|
||||
firstchar = nextGoodChar(false);
|
||||
if (firstchar == '"' || firstchar == '\'') return 0L;
|
||||
}
|
||||
long value = 0;
|
||||
final boolean negative = firstchar == '-';
|
||||
if (!negative) {
|
||||
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")");
|
||||
if (firstchar == '+') firstchar = nextChar(); //兼容+开头的
|
||||
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")");
|
||||
value = firstchar - '0';
|
||||
}
|
||||
if (firstchar == 'N') {
|
||||
if (negative) throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")");
|
||||
char c = nextChar();
|
||||
if (c != 'a') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'N') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
if (quote) {
|
||||
c = nextChar();
|
||||
if (c != '"' && c != '\'') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
}
|
||||
return 0L; //NaN 返回0;
|
||||
} else if (firstchar == 'I') { //Infinity
|
||||
char c = nextChar();
|
||||
if (c != 'n') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'f') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'i') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'n') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'i') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 't') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
c = nextChar();
|
||||
if (c != 'y') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
if (quote) {
|
||||
c = nextChar();
|
||||
if (c != '"' && c != '\'') throw new ConvertException("illegal escape(" + c + ") (position = " + position + ")");
|
||||
}
|
||||
return negative ? Long.MIN_VALUE : Long.MAX_VALUE;
|
||||
}
|
||||
boolean hex = false;
|
||||
boolean dot = false;
|
||||
for (;;) {
|
||||
if (currpos == eof) break;
|
||||
char ch = text0[++currpos];
|
||||
int val = digits[ch];
|
||||
if (quote && val == -3) continue;
|
||||
if (val <= -3) break;
|
||||
if (dot) continue;
|
||||
if (val == -1) {
|
||||
if (ch == '.') {
|
||||
dot = true;
|
||||
continue;
|
||||
}
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
|
||||
char ch = nextChar();
|
||||
if (ch == 0) break;
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (dot) continue;
|
||||
value = (hex ? (value << 4) : ((value << 3) + (value << 1))) + digits[ch];
|
||||
} else if (ch == '"' || ch == '\'') {
|
||||
if (quote) break;
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
} else if (ch == 'x' || ch == 'X') {
|
||||
if (value != 0) throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
hex = true;
|
||||
} else if (ch >= 'a' && ch <= 'f') {
|
||||
if (!hex) throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
if (dot) continue;
|
||||
value = (value << 4) + digits[ch];
|
||||
} else if (ch >= 'A' && ch <= 'F') {
|
||||
if (!hex) throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
if (dot) continue;
|
||||
value = (value << 4) + digits[ch];
|
||||
} else if (quote && ch <= ' ') {
|
||||
} else if (ch == '.') {
|
||||
dot = true;
|
||||
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
|
||||
backChar(ch);
|
||||
break;
|
||||
} else {
|
||||
throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")");
|
||||
}
|
||||
if (val != -2) value = value * 10 + val;
|
||||
}
|
||||
this.position = currpos - 1;
|
||||
return negative ? -value : value;
|
||||
}
|
||||
|
||||
public final String readFieldName() {
|
||||
return this.readSmallString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DeMember readFieldName(final DeMember[] members) {
|
||||
final String exceptedfield = this.readSmallString();
|
||||
if (exceptedfield == null) return null;
|
||||
public final DeMember readFieldName(final DeMember[] members, Map<String, DeMember> memberFieldMap, Map<Integer, DeMember> memberTagMap) {
|
||||
final String exceptedField = this.readSmallString();
|
||||
if (exceptedField == null) return null;
|
||||
final int len = members.length;
|
||||
if (this.fieldIndex >= len) this.fieldIndex = 0;
|
||||
for (int k = this.fieldIndex; k < len; k++) {
|
||||
if (exceptedfield.equals(members[k].getAttribute().field())) {
|
||||
if (exceptedField.equals(members[k].getAttribute().field())) {
|
||||
this.fieldIndex = k;
|
||||
return members[k];
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < this.fieldIndex; k++) {
|
||||
if (exceptedfield.equals(members[k].getAttribute().field())) {
|
||||
if (exceptedField.equals(members[k].getAttribute().field())) {
|
||||
this.fieldIndex = k;
|
||||
return members[k];
|
||||
}
|
||||
@@ -557,7 +637,11 @@ public class JsonReader extends Reader {
|
||||
String chars = readSmallString();
|
||||
if (chars != null) chars = chars.trim();
|
||||
if (chars == null || chars.isEmpty()) return 0.f;
|
||||
return Float.parseFloat(chars);
|
||||
switch (chars) {
|
||||
case "Infinity": return (float) Double.POSITIVE_INFINITY;
|
||||
case "-Infinity": return (float) Double.NEGATIVE_INFINITY;
|
||||
default: return Float.parseFloat(chars); //Float.parseFloat能识别NaN
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -565,7 +649,11 @@ public class JsonReader extends Reader {
|
||||
String chars = readSmallString();
|
||||
if (chars != null) chars = chars.trim();
|
||||
if (chars == null || chars.isEmpty()) return 0.0;
|
||||
return Double.parseDouble(chars);
|
||||
switch (chars) {
|
||||
case "Infinity": return Double.POSITIVE_INFINITY;
|
||||
case "-Infinity": return Double.NEGATIVE_INFINITY;
|
||||
default: return Double.parseDouble(chars); //Double.parseDouble能识别NaN
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,14 +664,8 @@ public class JsonReader extends Reader {
|
||||
@Override
|
||||
public String readString() {
|
||||
final char[] text0 = this.text;
|
||||
char expected = nextGoodChar(true);
|
||||
int currpos = this.position;
|
||||
char expected = text0[++currpos];
|
||||
if (expected <= ' ') {
|
||||
for (;;) {
|
||||
expected = text0[++currpos];
|
||||
if (expected > ' ') break;
|
||||
}
|
||||
}
|
||||
if (expected != '"' && expected != '\'') {
|
||||
if (expected == 'n' && text0.length > currpos + 3 && (text0[1 + currpos] == 'u' && text0[2 + currpos] == 'l' && text0[3 + currpos] == 'l')) {
|
||||
if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') {
|
||||
|
||||
@@ -88,7 +88,7 @@ class JsonStreamWriter extends JsonByteBufferWriter {
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String
|
||||
*
|
||||
* @param quote 是否写入双引号
|
||||
* @param value String值
|
||||
|
||||
@@ -21,7 +21,7 @@ public abstract class JsonWriter extends Writer {
|
||||
|
||||
protected static final int defaultSize = Integer.getInteger("redkale.convert.json.writer.buffer.defsize", Integer.getInteger("redkale.convert.writer.buffer.defsize", 1024));
|
||||
|
||||
protected boolean tiny;
|
||||
protected boolean tiny = JsonFactory.root().tiny();
|
||||
|
||||
@Override
|
||||
public boolean tiny() {
|
||||
@@ -47,7 +47,7 @@ public abstract class JsonWriter extends Writer {
|
||||
public abstract void writeTo(final byte[] chs, final int start, final int len); //只能是 0 - 127 的字符
|
||||
|
||||
/**
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String
|
||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String
|
||||
*
|
||||
* @param quote 是否加双引号
|
||||
* @param value 非null且不含需要转义的字符的String值
|
||||
@@ -85,6 +85,8 @@ public abstract class JsonWriter extends Writer {
|
||||
@Override
|
||||
public abstract void writeLong(long value);
|
||||
|
||||
public abstract void writeString(final boolean quote, String value);
|
||||
|
||||
@Override
|
||||
public abstract void writeString(String value);
|
||||
|
||||
@@ -156,7 +158,7 @@ public abstract class JsonWriter extends Writer {
|
||||
|
||||
@Override
|
||||
public final void writeWrapper(StringWrapper value) {
|
||||
writeLatin1To(false, String.valueOf(value));
|
||||
writeString(false, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -94,7 +94,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
|
||||
}
|
||||
final Map<String, String> clientHeaders = new LinkedHashMap<>();
|
||||
byte[] clientBody = null;
|
||||
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
|
||||
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC, "true");
|
||||
if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
|
||||
if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
|
||||
if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
|
||||
@@ -148,7 +148,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
|
||||
}
|
||||
final Map<String, String> clientHeaders = new LinkedHashMap<>();
|
||||
byte[] clientBody = null;
|
||||
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
|
||||
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC, "true");
|
||||
if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
|
||||
if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
|
||||
if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.redkale.boot.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.util.Traces;
|
||||
|
||||
/**
|
||||
* 没有配置MQ且也没有ClusterAgent的情况下实现的默认HttpMessageClient实例
|
||||
@@ -100,8 +101,9 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
future.completeExceptionally(new RuntimeException("404 Not Found " + topic));
|
||||
return future;
|
||||
}
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request);
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request, userid);
|
||||
HttpResponse resp = new HttpMessageLocalResponse(req, future);
|
||||
Traces.createTraceid();
|
||||
try {
|
||||
servlet.execute(req, resp);
|
||||
} catch (Exception e) {
|
||||
@@ -117,9 +119,10 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
if (fine) logger.log(Level.FINE, "sendMessage: request=" + request + ", not found servlet");
|
||||
return CompletableFuture.completedFuture(new HttpResult().status(404));
|
||||
}
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request);
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request, userid);
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
HttpResponse resp = new HttpMessageLocalResponse(req, future);
|
||||
Traces.createTraceid();
|
||||
try {
|
||||
servlet.execute(req, resp);
|
||||
} catch (Exception e) {
|
||||
@@ -144,8 +147,9 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
if (fine) logger.log(Level.FINE, "produceMessage: request=" + request + ", not found servlet");
|
||||
return;
|
||||
}
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request);
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request, userid);
|
||||
HttpResponse resp = new HttpMessageLocalResponse(req, null);
|
||||
Traces.createTraceid();
|
||||
try {
|
||||
servlet.execute(req, resp);
|
||||
} catch (Exception e) {
|
||||
@@ -156,8 +160,9 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
@Override
|
||||
public void broadcastMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
|
||||
HttpPrepareServlet ps = prepareServlet();
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request);
|
||||
HttpRequest req = new HttpMessageLocalRequest(context(), request, userid);
|
||||
HttpResponse resp = new HttpMessageLocalResponse(req, null);
|
||||
Traces.createTraceid();
|
||||
ps.filterServletsByMmcTopic(topic).forEach(s -> {
|
||||
try {
|
||||
s.execute(req, resp);
|
||||
@@ -169,8 +174,9 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
|
||||
public static class HttpMessageLocalRequest extends HttpRequest {
|
||||
|
||||
public HttpMessageLocalRequest(HttpContext context, HttpSimpleRequest req) {
|
||||
public HttpMessageLocalRequest(HttpContext context, HttpSimpleRequest req, Serializable userid) {
|
||||
super(context, req);
|
||||
if (userid != null) this.currentUserid = userid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +224,17 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
@Override
|
||||
public void finish(final Convert convert, final Type type, Object obj) {
|
||||
if (future == null) return;
|
||||
future.complete(obj);
|
||||
if (obj instanceof CompletableFuture) {
|
||||
((CompletableFuture) obj).whenComplete((r, t) -> {
|
||||
if (t == null) {
|
||||
future.complete(r);
|
||||
} else {
|
||||
future.completeExceptionally((Throwable) t);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
future.complete(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,11 +43,11 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
|
||||
protected final HttpServlet servlet;
|
||||
|
||||
protected final boolean multiconsumer;
|
||||
protected final boolean multiConsumer;
|
||||
|
||||
protected final String restmodule; // 前后有/, 例如: /user/
|
||||
protected final String restModule; // 前后有/, 例如: /user/
|
||||
|
||||
protected final String multimodule; // 前后有/, 例如: /userstat/
|
||||
protected final String multiModule; // 前后有/, 例如: /userstat/
|
||||
|
||||
protected ThreadLocal<ObjectPool<HttpMessageResponse>> respPoolThreadLocal;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
|
||||
protected CountDownLatch cdl;
|
||||
|
||||
protected long starttime;
|
||||
protected long startTime;
|
||||
|
||||
protected final Runnable innerCallback = () -> {
|
||||
if (cdl != null) cdl.countDown();
|
||||
@@ -74,9 +74,9 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
this.service = service;
|
||||
this.servlet = servlet;
|
||||
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
|
||||
this.multiconsumer = mmc != null;
|
||||
this.restmodule = "/" + Rest.getRestModule(service) + "/";
|
||||
this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null;
|
||||
this.multiConsumer = mmc != null;
|
||||
this.restModule = "/" + Rest.getRestModule(service) + "/";
|
||||
this.multiModule = mmc != null ? ("/" + mmc.module() + "/") : null;
|
||||
this.respSupplier = () -> respPoolThreadLocal.get().get();
|
||||
this.respConsumer = resp -> respPoolThreadLocal.get().accept(resp);
|
||||
this.respPoolThreadLocal = ThreadLocal.withInitial(() -> ObjectPool.createUnsafePool(Utility.cpus(),
|
||||
@@ -85,7 +85,7 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
|
||||
@Override
|
||||
public void begin(final int size, long starttime) {
|
||||
this.starttime = starttime;
|
||||
this.startTime = starttime;
|
||||
this.cdl = size > 1 ? new CountDownLatch(size) : null;
|
||||
}
|
||||
|
||||
@@ -97,15 +97,16 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
private void execute(final MessageRecord message, final Runnable callback) {
|
||||
HttpMessageRequest request = null;
|
||||
try {
|
||||
Traces.currTraceid(message.getTraceid());
|
||||
long now = System.currentTimeMillis();
|
||||
long cha = now - message.createtime;
|
||||
long e = now - starttime;
|
||||
if (multiconsumer) message.setResptopic(null); //不容许有响应
|
||||
long cha = now - message.createTime;
|
||||
long e = now - startTime;
|
||||
if (multiConsumer) message.setRespTopic(null); //不容许有响应
|
||||
|
||||
HttpMessageResponse response = respSupplier.get();
|
||||
request = response.request();
|
||||
response.prepare(message, callback, producers.getProducer(message));
|
||||
if (multiconsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
|
||||
if (multiConsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multiModule, this.restModule));
|
||||
|
||||
server.getHttpServer().getContext().execute(servlet, request, response);
|
||||
long o = System.currentTimeMillis() - now;
|
||||
@@ -117,9 +118,9 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
if (message.getResptopic() != null && !message.getResptopic().isEmpty()) {
|
||||
if (message.getRespTopic() != null && !message.getRespTopic().isEmpty()) {
|
||||
HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(),
|
||||
null, message, callback, messageClient, producers.getProducer(message), message.getResptopic(), new HttpResult().status(500));
|
||||
null, message, callback, messageClient, producers.getProducer(message), message.getRespTopic(), new HttpResult().status(500));
|
||||
}
|
||||
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex);
|
||||
}
|
||||
@@ -131,7 +132,7 @@ public class HttpMessageProcessor implements MessageProcessor {
|
||||
try {
|
||||
this.cdl.await(30, TimeUnit.SECONDS);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " commit error, restmodule=" + this.restmodule, ex);
|
||||
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " commit error, restmodule=" + this.restModule, ex);
|
||||
}
|
||||
this.cdl = null;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class HttpMessageRequest extends HttpRequest {
|
||||
this.message = message;
|
||||
this.hashid = this.message.hash();
|
||||
this.currentUserid = message.getUserid();
|
||||
this.createtime = System.currentTimeMillis();
|
||||
this.createTime = System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,11 +69,11 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
}
|
||||
|
||||
public void finishHttpResult(Type type, HttpResult result) {
|
||||
finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), type, this.message, this.callback, this.messageClient, this.producer, message.getResptopic(), result);
|
||||
finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), type, this.message, this.callback, this.messageClient, this.producer, message.getRespTopic(), result);
|
||||
}
|
||||
|
||||
public void finishHttpResult(Type type, Convert respConvert, HttpResult result) {
|
||||
finishHttpResult(this.finest, respConvert == null ? ((HttpMessageRequest) this.request).getRespConvert() : respConvert, type, this.message, this.callback, this.messageClient, this.producer, message.getResptopic(), result);
|
||||
finishHttpResult(this.finest, respConvert == null ? ((HttpMessageRequest) this.request).getRespConvert() : respConvert, type, this.message, this.callback, this.messageClient, this.producer, message.getRespTopic(), result);
|
||||
}
|
||||
|
||||
public static void finishHttpResult(boolean finest, Convert respConvert, Type type, MessageRecord msg, Runnable callback, MessageClient messageClient, MessageProducer producer, String resptopic, HttpResult result) {
|
||||
@@ -120,7 +120,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finishJson(final JsonConvert convert, final Object obj) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finishJson(final Type type, final Object obj) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(Type type, org.redkale.service.RetResult ret) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -158,7 +158,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(final Convert convert, Type type, org.redkale.service.RetResult ret) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(final Convert convert, final Type type, Object obj) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -176,7 +176,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(String obj) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
} else if (finest) {
|
||||
producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
|
||||
}
|
||||
if (this.message.isEmptyResptopic()) {
|
||||
if (this.message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -219,7 +219,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(final Convert convert, Type type, HttpResult result) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -229,7 +229,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(boolean kill, final byte[] bs, int offset, int length) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -242,7 +242,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -252,7 +252,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
protected <A> void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length, Consumer<A> consumer, A attachment) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(boolean kill, ByteBuffer buffer) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
@@ -273,7 +273,7 @@ public class HttpMessageResponse extends HttpResponse {
|
||||
|
||||
@Override
|
||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
||||
if (message.isEmptyResptopic()) {
|
||||
if (message.isEmptyRespTopic()) {
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,14 +58,31 @@ public abstract class MessageAgent {
|
||||
|
||||
protected int producerCount = 1;
|
||||
|
||||
protected MessageCoder<MessageRecord> messageCoder = MessageRecordCoder.getInstance();
|
||||
|
||||
//本地Service消息接收处理器, key:consumer
|
||||
protected HashMap<String, MessageConsumerNode> messageNodes = new LinkedHashMap<>();
|
||||
|
||||
public void init(AnyValue config) {
|
||||
public void init(ResourceFactory factory, AnyValue config) {
|
||||
this.name = checkName(config.getValue("name", ""));
|
||||
this.httpMessageClient = new HttpMessageClient(this);
|
||||
this.sncpMessageClient = new SncpMessageClient(this);
|
||||
this.producerCount = config.getIntValue("producers", Utility.cpus());
|
||||
String coderType = config.getValue("coder", "");
|
||||
if (!coderType.trim().isEmpty()) {
|
||||
try {
|
||||
Class<MessageCoder<MessageRecord>> coderClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(coderType);
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(coderClass, coderClass.getName());
|
||||
MessageCoder<MessageRecord> coder = coderClass.getConstructor().newInstance();
|
||||
if (factory != null) factory.inject(coder);
|
||||
if (coder instanceof Service) ((Service) coder).init(config);
|
||||
this.messageCoder = coder;
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// application (it doesn't execute completion handlers).
|
||||
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
|
||||
Thread t = new Thread(r);
|
||||
@@ -102,6 +119,7 @@ public abstract class MessageAgent {
|
||||
if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown();
|
||||
if (this.sncpProducer != null) this.sncpProducer.shutdown().join();
|
||||
if (this.httpProducer != null) this.httpProducer.shutdown().join();
|
||||
if (this.messageCoder instanceof Service) ((Service) this.messageCoder).destroy(config);
|
||||
}
|
||||
|
||||
protected List<MessageConsumer> getAllMessageConsumer() {
|
||||
@@ -125,6 +143,10 @@ public abstract class MessageAgent {
|
||||
return producers;
|
||||
}
|
||||
|
||||
public MessageCoder<MessageRecord> getMessageCoder() {
|
||||
return this.messageCoder;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.redkale.convert.Convert;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import static org.redkale.mq.MessageRecord.*;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -76,7 +77,7 @@ public abstract class MessageClient {
|
||||
if (node.scheduledFuture != null) node.scheduledFuture.cancel(true);
|
||||
AtomicLong ncer = node.getCounter();
|
||||
if (ncer != null) ncer.decrementAndGet();
|
||||
final long cha = now - msg.createtime;
|
||||
final long cha = now - msg.createTime;
|
||||
if (finest) messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.receive (mq.delay = " + cha + "ms, mq.seqid = " + msg.getSeqid() + ")");
|
||||
node.future.complete(msg);
|
||||
long cha2 = System.currentTimeMillis() - now;
|
||||
@@ -97,8 +98,8 @@ public abstract class MessageClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needresp && (message.getResptopic() == null || message.getResptopic().isEmpty())) {
|
||||
message.setResptopic(respTopic);
|
||||
if (needresp && (message.getRespTopic() == null || message.getRespTopic().isEmpty())) {
|
||||
message.setRespTopic(respTopic);
|
||||
}
|
||||
if (counter != null) counter.incrementAndGet();
|
||||
getProducer().apply(message);
|
||||
@@ -126,47 +127,59 @@ public abstract class MessageClient {
|
||||
protected abstract MessageProducers getProducer();
|
||||
|
||||
public MessageRecord createMessageRecord(String resptopic, String content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, null, resptopic, Traces.createTraceid(), content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(String topic, String resptopic, String content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, Traces.createTraceid(), content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(String topic, String resptopic, String traceid, String content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, traceid, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(int userid, String topic, String resptopic, String content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, Traces.createTraceid(), content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(int userid, String topic, String resptopic, String traceid, String content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, traceid, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(String topic, String resptopic, Convert convert, Object bean) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, convert.convertToBytes(bean));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, Traces.createTraceid(), convert.convertToBytes(bean));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(String topic, String resptopic, String traceid, Convert convert, Object bean) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, traceid, convert.convertToBytes(bean));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, convert.convertToBytes(bean));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, Traces.createTraceid(), convert.convertToBytes(bean));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, groupid, topic, resptopic, Traces.createTraceid(), convert.convertToBytes(bean));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean));
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, Traces.createTraceid(), convert.convertToBytes(bean));
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(String topic, String resptopic, byte[] content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), (byte) 0, topic, resptopic, content);
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), (byte) 0, topic, resptopic, Traces.createTraceid(), content);
|
||||
}
|
||||
|
||||
public MessageRecord createMessageRecord(long seqid, String topic, String resptopic, byte[] content) {
|
||||
return new MessageRecord(seqid, (byte) 0, topic, resptopic, content);
|
||||
return new MessageRecord(seqid, (byte) 0, topic, resptopic, Traces.createTraceid(), content);
|
||||
}
|
||||
|
||||
protected MessageRecord createMessageRecord(byte ctype, String topic, String resptopic, byte[] content) {
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype, topic, resptopic, content);
|
||||
return new MessageRecord(msgSeqno.incrementAndGet(), ctype, topic, resptopic, Traces.createTraceid(), content);
|
||||
}
|
||||
|
||||
protected MessageRecord createMessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) {
|
||||
return new MessageRecord(seqid, ctype, topic, resptopic, content);
|
||||
return new MessageRecord(seqid, ctype, topic, resptopic, Traces.createTraceid(), content);
|
||||
}
|
||||
|
||||
private byte ctype(Convert convert, Object bean) {
|
||||
|
||||
@@ -52,7 +52,7 @@ public class MessageRecord implements Serializable {
|
||||
|
||||
@ConvertColumn(index = 4)
|
||||
@Comment("创建时间")
|
||||
protected long createtime;
|
||||
protected long createTime;
|
||||
|
||||
@ConvertColumn(index = 5)
|
||||
@Comment("用户ID,无用户信息视为null或0, 具体数据类型只能是int、long、String") //@since 2.5.0 由int改成Serializable
|
||||
@@ -68,40 +68,45 @@ public class MessageRecord implements Serializable {
|
||||
|
||||
@ConvertColumn(index = 8)
|
||||
@Comment("目标topic, 为空表示无目标topic")
|
||||
protected String resptopic;
|
||||
protected String respTopic;
|
||||
|
||||
@ConvertColumn(index = 9)
|
||||
@Comment("链路ID")
|
||||
protected String traceid;
|
||||
|
||||
@ConvertColumn(index = 10)
|
||||
@Comment("消息内容")
|
||||
protected byte[] content;
|
||||
|
||||
@ConvertColumn(index = 10)
|
||||
@ConvertColumn(index = 11)
|
||||
@Comment("消息内容的类型")
|
||||
protected byte ctype;
|
||||
|
||||
@Comment("本地附加对象,不会被序列化")
|
||||
protected Object localattach;
|
||||
protected Object localAttach;
|
||||
|
||||
public MessageRecord() {
|
||||
}
|
||||
|
||||
protected MessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) {
|
||||
this(seqid, ctype, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content);
|
||||
protected MessageRecord(long seqid, byte ctype, String topic, String resptopic, String traceid, byte[] content) {
|
||||
this(seqid, ctype, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, traceid, content);
|
||||
}
|
||||
|
||||
protected MessageRecord(long seqid, byte ctype, int flag, Serializable userid, String groupid, String topic, String resptopic, byte[] content) {
|
||||
this(seqid, ctype, 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content);
|
||||
protected MessageRecord(long seqid, byte ctype, int flag, Serializable userid, String groupid, String topic, String resptopic, String traceid, byte[] content) {
|
||||
this(seqid, ctype, 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, traceid, content);
|
||||
}
|
||||
|
||||
protected MessageRecord(long seqid, byte ctype, int version, int flag, long createtime, Serializable userid, String groupid, String topic, String resptopic, byte[] content) {
|
||||
protected MessageRecord(long seqid, byte ctype, int version, int flag, long createTime, Serializable userid, String groupid, String topic, String resptopic, String traceid, byte[] content) {
|
||||
this.seqid = seqid;
|
||||
this.ctype = ctype;
|
||||
this.version = version;
|
||||
this.flag = flag;
|
||||
this.createtime = createtime;
|
||||
this.createTime = createTime;
|
||||
this.userid = userid;
|
||||
this.groupid = groupid;
|
||||
this.topic = topic;
|
||||
this.resptopic = resptopic;
|
||||
this.respTopic = resptopic;
|
||||
this.traceid = traceid;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@@ -110,7 +115,7 @@ public class MessageRecord implements Serializable {
|
||||
}
|
||||
|
||||
public MessageRecord attach(Object attach) {
|
||||
this.localattach = attach;
|
||||
this.localAttach = attach;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -120,8 +125,13 @@ public class MessageRecord implements Serializable {
|
||||
}
|
||||
|
||||
@ConvertDisabled
|
||||
public boolean isEmptyResptopic() {
|
||||
return this.resptopic == null || this.resptopic.isEmpty();
|
||||
public boolean isEmptyRespTopic() {
|
||||
return this.respTopic == null || this.respTopic.isEmpty();
|
||||
}
|
||||
|
||||
@ConvertDisabled
|
||||
public boolean isEmptyTraceid() {
|
||||
return this.traceid == null || this.traceid.isEmpty();
|
||||
}
|
||||
|
||||
public <T> T convertFromContent(Convert convert, java.lang.reflect.Type type) {
|
||||
@@ -159,8 +169,8 @@ public class MessageRecord implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageRecord createtime(long createtime) {
|
||||
this.createtime = createtime;
|
||||
public MessageRecord createTime(long createtime) {
|
||||
this.createTime = createtime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -179,8 +189,8 @@ public class MessageRecord implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageRecord resptopic(String resptopic) {
|
||||
this.resptopic = resptopic;
|
||||
public MessageRecord respTopic(String resptopic) {
|
||||
this.respTopic = resptopic;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -189,6 +199,11 @@ public class MessageRecord implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageRecord traceid(String traceid) {
|
||||
this.traceid = traceid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageRecord contentString(String content) {
|
||||
this.content = content == null ? null : content.getBytes(StandardCharsets.UTF_8);
|
||||
return this;
|
||||
@@ -218,12 +233,12 @@ public class MessageRecord implements Serializable {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public long getCreatetime() {
|
||||
return createtime;
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreatetime(long createtime) {
|
||||
this.createtime = createtime;
|
||||
public void setCreateTime(long createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public Serializable getUserid() {
|
||||
@@ -234,6 +249,14 @@ public class MessageRecord implements Serializable {
|
||||
this.userid = userid;
|
||||
}
|
||||
|
||||
public String getTraceid() {
|
||||
return traceid;
|
||||
}
|
||||
|
||||
public void setTraceid(String traceid) {
|
||||
this.traceid = traceid;
|
||||
}
|
||||
|
||||
public String getGroupid() {
|
||||
return groupid;
|
||||
}
|
||||
@@ -250,12 +273,12 @@ public class MessageRecord implements Serializable {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public String getResptopic() {
|
||||
return resptopic;
|
||||
public String getRespTopic() {
|
||||
return respTopic;
|
||||
}
|
||||
|
||||
public void setResptopic(String resptopic) {
|
||||
this.resptopic = resptopic;
|
||||
public void setRespTopic(String respTopic) {
|
||||
this.respTopic = respTopic;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
@@ -273,11 +296,11 @@ public class MessageRecord implements Serializable {
|
||||
sb.append("{\"seqid\":").append(this.seqid);
|
||||
sb.append(",\"version\":").append(this.version);
|
||||
if (this.flag != 0) sb.append(",\"flag\":").append(this.flag);
|
||||
if (this.createtime != 0) sb.append(",\"createtime\":").append(this.createtime);
|
||||
if (this.createTime != 0) sb.append(",\"createTime\":").append(this.createTime);
|
||||
if (this.userid != null) sb.append(",\"userid\":").append(this.userid);
|
||||
if (this.groupid != null) sb.append(",\"groupid\":\"").append(this.groupid).append("\"");
|
||||
if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\"");
|
||||
if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\"");
|
||||
if (this.respTopic != null) sb.append(",\"respTopic\":\"").append(this.respTopic).append("\"");
|
||||
if (this.content != null) {
|
||||
if (this.ctype == CTYPE_BSON_RESULT && this.content.length > SncpRequest.HEADER_SIZE) {
|
||||
int offset = SncpRequest.HEADER_SIZE + 1; //循环占位符
|
||||
@@ -292,8 +315,8 @@ public class MessageRecord implements Serializable {
|
||||
sb.append(",\"content\":").append(req);
|
||||
} else if (this.ctype == CTYPE_HTTP_RESULT) {
|
||||
sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content));
|
||||
} else if (localattach != null) {
|
||||
sb.append(",\"attach\":").append(JsonConvert.root().convertTo(localattach));
|
||||
} else if (localAttach != null) {
|
||||
sb.append(",\"attach\":").append(JsonConvert.root().convertTo(localAttach));
|
||||
} else {
|
||||
sb.append(",\"content\":\"").append(new String(this.content, StandardCharsets.UTF_8)).append("\"");
|
||||
}
|
||||
|
||||
@@ -29,10 +29,11 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
|
||||
@Override
|
||||
public byte[] encode(MessageRecord data) {
|
||||
if (data == null) return null;
|
||||
byte[] stopics = MessageCoder.getBytes(data.getTopic());
|
||||
byte[] dtopics = MessageCoder.getBytes(data.getResptopic());
|
||||
byte[] groupid = MessageCoder.getBytes(data.getGroupid());
|
||||
byte[] userid = MessageCoder.encodeUserid(data.getUserid());
|
||||
byte[] groupid = MessageCoder.getBytes(data.getGroupid());
|
||||
byte[] topic = MessageCoder.getBytes(data.getTopic());
|
||||
byte[] resptopic = MessageCoder.getBytes(data.getRespTopic());
|
||||
byte[] traceid = MessageCoder.getBytes(data.getTraceid());
|
||||
int count = 8 //seqid
|
||||
+ 1 //ctype
|
||||
+ 4 //version
|
||||
@@ -40,8 +41,9 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
|
||||
+ 8 //createtime
|
||||
+ 2 + userid.length
|
||||
+ 2 + groupid.length
|
||||
+ 2 + stopics.length
|
||||
+ 2 + dtopics.length
|
||||
+ 2 + topic.length
|
||||
+ 2 + resptopic.length
|
||||
+ 2 + traceid.length
|
||||
+ 4 + (data.getContent() == null ? 0 : data.getContent().length);
|
||||
final byte[] bs = new byte[count];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bs);
|
||||
@@ -49,15 +51,23 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
|
||||
buffer.put(data.ctype);
|
||||
buffer.putInt(data.getVersion());
|
||||
buffer.putInt(data.getFlag());
|
||||
buffer.putLong(data.getCreatetime());
|
||||
buffer.putLong(data.getCreateTime());
|
||||
|
||||
buffer.putChar((char) userid.length);
|
||||
if (userid.length > 0) buffer.put(userid);
|
||||
|
||||
buffer.putChar((char) groupid.length);
|
||||
if (groupid.length > 0) buffer.put(groupid);
|
||||
buffer.putChar((char) stopics.length);
|
||||
if (stopics.length > 0) buffer.put(stopics);
|
||||
buffer.putChar((char) dtopics.length);
|
||||
if (dtopics.length > 0) buffer.put(dtopics);
|
||||
|
||||
buffer.putChar((char) topic.length);
|
||||
if (topic.length > 0) buffer.put(topic);
|
||||
|
||||
buffer.putChar((char) resptopic.length);
|
||||
if (resptopic.length > 0) buffer.put(resptopic);
|
||||
|
||||
buffer.putChar((char) traceid.length);
|
||||
if (traceid.length > 0) buffer.put(traceid);
|
||||
|
||||
if (data.getContent() == null) {
|
||||
buffer.putInt(0);
|
||||
} else {
|
||||
@@ -81,6 +91,7 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
|
||||
String groupid = MessageCoder.getShortString(buffer);
|
||||
String topic = MessageCoder.getShortString(buffer);
|
||||
String resptopic = MessageCoder.getShortString(buffer);
|
||||
String traceid = MessageCoder.getShortString(buffer);
|
||||
|
||||
byte[] content = null;
|
||||
int contentlen = buffer.getInt();
|
||||
@@ -88,7 +99,7 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
|
||||
content = new byte[contentlen];
|
||||
buffer.get(content);
|
||||
}
|
||||
return new MessageRecord(seqid, ctype, version, flag, createtime, userid, groupid, topic, resptopic, content);
|
||||
return new MessageRecord(seqid, ctype, version, flag, createtime, userid, groupid, topic, resptopic, traceid, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class MessageRespFutureNode implements Runnable {
|
||||
|
||||
protected final long seqid;
|
||||
|
||||
protected final long createtime;
|
||||
protected final long createTime;
|
||||
|
||||
protected final AtomicLong counter;
|
||||
|
||||
@@ -44,14 +44,14 @@ public class MessageRespFutureNode implements Runnable {
|
||||
this.respNodes = respNodes;
|
||||
this.counter = counter;
|
||||
this.future = future;
|
||||
this.createtime = System.currentTimeMillis();
|
||||
this.createTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override //超时后被timeoutExecutor调用
|
||||
public void run() { //timeout
|
||||
respNodes.remove(this.seqid);
|
||||
future.completeExceptionally(new TimeoutException());
|
||||
logger.log(Level.WARNING, getClass().getSimpleName() + " wait msg: " + message + " timeout " + (System.currentTimeMillis() - createtime) + "ms"
|
||||
logger.log(Level.WARNING, getClass().getSimpleName() + " wait msg: " + message + " timeout " + (System.currentTimeMillis() - createTime) + "ms"
|
||||
+ (message.userid != null || (message.groupid != null && !message.groupid.isEmpty()) ? (message.userid != null ? (", userid:" + message.userid) : (", groupid:" + message.groupid)) : ""));
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ public class MessageRespFutureNode implements Runnable {
|
||||
return seqid;
|
||||
}
|
||||
|
||||
public long getCreatetime() {
|
||||
return createtime;
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public AtomicLong getCounter() {
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.logging.*;
|
||||
import org.redkale.boot.NodeSncpServer;
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.util.Traces;
|
||||
|
||||
/**
|
||||
* 一个Service对应一个MessageProcessor
|
||||
@@ -75,8 +76,9 @@ public class SncpMessageProcessor implements MessageProcessor {
|
||||
private void execute(final MessageRecord message, final Runnable callback) {
|
||||
SncpMessageResponse response = null;
|
||||
try {
|
||||
Traces.currTraceid(message.getTraceid());
|
||||
long now = System.currentTimeMillis();
|
||||
long cha = now - message.createtime;
|
||||
long cha = now - message.createTime;
|
||||
long e = now - starttime;
|
||||
SncpContext context = server.getSncpServer().getContext();
|
||||
SncpMessageRequest request = new SncpMessageRequest(context, message);
|
||||
|
||||
@@ -29,13 +29,13 @@ public class SncpMessageRequest extends SncpRequest {
|
||||
super(context);
|
||||
this.message = message;
|
||||
this.hashid = this.message.hash();
|
||||
this.createtime = System.currentTimeMillis();
|
||||
this.createTime = System.currentTimeMillis();
|
||||
readHeader(ByteBuffer.wrap(message.getContent()), null);
|
||||
}
|
||||
|
||||
@Override //被SncpAsyncHandler.sncp_setParams调用
|
||||
protected void sncp_setParams(SncpDynServlet.SncpServletAction action, Logger logger, Object... params) {
|
||||
if (message.localattach != null) return;
|
||||
if (message.localAttach != null) return;
|
||||
if (logger.isLoggable(Level.FINER)) {
|
||||
message.attach(Utility.append(new Object[]{action.actionName()}, params));
|
||||
} else {
|
||||
|
||||
@@ -51,12 +51,12 @@ public class SncpMessageResponse extends SncpResponse {
|
||||
if (out == null) {
|
||||
final ByteArray result = new ByteArray(SncpRequest.HEADER_SIZE);
|
||||
fillHeader(result, 0, retcode);
|
||||
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, (byte[]) null));
|
||||
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getRespTopic(), null, (byte[]) null));
|
||||
return;
|
||||
}
|
||||
final int respBodyLength = out.count(); //body总长度
|
||||
final ByteArray result = out.toByteArray();
|
||||
fillHeader(result, respBodyLength - HEADER_SIZE, retcode);
|
||||
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, result.getBytes()));
|
||||
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getRespTopic(), null, result.getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,14 @@ public abstract class AsyncConnection implements ChannelContext, Channel, AutoCl
|
||||
ioThread.execute(command);
|
||||
}
|
||||
|
||||
public final void execute(Runnable... commands) {
|
||||
ioThread.execute(commands);
|
||||
}
|
||||
|
||||
public final void execute(Collection<Runnable> commands) {
|
||||
ioThread.execute(commands);
|
||||
}
|
||||
|
||||
public final boolean inCurrThread() {
|
||||
return ioThread.inCurrThread();
|
||||
}
|
||||
|
||||
@@ -67,6 +67,15 @@ public class AsyncIOThread extends AsyncThread {
|
||||
selector.wakeup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Collection<Runnable> commands) {
|
||||
if (commands == null) return;
|
||||
for (Runnable command : commands) {
|
||||
commandQueue.offer(command);
|
||||
}
|
||||
selector.wakeup();
|
||||
}
|
||||
|
||||
public void register(Consumer<Selector> consumer) {
|
||||
registerQueue.offer(consumer);
|
||||
selector.wakeup();
|
||||
|
||||
@@ -215,6 +215,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
|
||||
public final void prepare(final R request, final P response) {
|
||||
try {
|
||||
Traces.createTraceid();
|
||||
request.prepare();
|
||||
response.filter = this.headFilter;
|
||||
response.servlet = this;
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.function.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* 一个AsyncConnection绑定一个ProtocolCodec实例
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.net;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.ConvertDisabled;
|
||||
import org.redkale.convert.bson.BsonConvert;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
|
||||
@@ -28,7 +29,7 @@ public abstract class Request<C extends Context> {
|
||||
|
||||
protected final JsonConvert jsonConvert;
|
||||
|
||||
protected long createtime;
|
||||
protected long createTime;
|
||||
|
||||
protected boolean keepAlive;
|
||||
|
||||
@@ -80,7 +81,7 @@ public abstract class Request<C extends Context> {
|
||||
|
||||
protected void recycle() {
|
||||
hashid = 0;
|
||||
createtime = 0;
|
||||
createTime = 0;
|
||||
pipelineIndex = 0;
|
||||
pipelineCount = 0;
|
||||
pipelineOver = false;
|
||||
@@ -139,8 +140,14 @@ public abstract class Request<C extends Context> {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@Deprecated //since 2.7.0 replace by getCreateTime()
|
||||
@ConvertDisabled
|
||||
public long getCreatetime() {
|
||||
return createtime;
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public int getHashid() {
|
||||
|
||||
@@ -143,7 +143,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
protected void init(AsyncConnection channel) {
|
||||
this.channel = channel;
|
||||
this.request.channel = channel;
|
||||
this.request.createtime = System.currentTimeMillis();
|
||||
this.request.createTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
protected void setFilter(Filter<C, R, Response<C, R>> filter) {
|
||||
|
||||
@@ -269,8 +269,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
|
||||
public void start() throws IOException {
|
||||
this.prepare.init(this.context, config); //不能在init方法内执行,因Server.init执行后会调用loadService,loadServlet, 再执行Server.start
|
||||
this.postPrepareInit();
|
||||
this.serverChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader);
|
||||
if (application != null) { //main函数调试时可能为nulli
|
||||
if (application != null) { //main函数调试时可能为null
|
||||
application.getResourceFactory().inject(this.serverChannel);
|
||||
}
|
||||
this.serverChannel.open(config);
|
||||
@@ -284,6 +285,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
|
||||
}
|
||||
|
||||
protected void postPrepareInit() {
|
||||
}
|
||||
|
||||
protected void postStart() {
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.*;
|
||||
import org.redkale.util.ThreadHashExecutor;
|
||||
|
||||
@@ -62,6 +63,19 @@ public class WorkThread extends Thread implements Executor {
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(Collection<Runnable> commands) {
|
||||
if (commands == null) return;
|
||||
if (workExecutor == null) {
|
||||
for (Runnable command : commands) {
|
||||
command.run();
|
||||
}
|
||||
} else {
|
||||
for (Runnable command : commands) {
|
||||
workExecutor.execute(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void runAsync(Runnable command) {
|
||||
if (workExecutor == null) {
|
||||
ForkJoinPool.commonPool().execute(command);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
package org.redkale.net.client;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
@@ -27,6 +26,8 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public abstract class Client<R extends ClientRequest, P> {
|
||||
|
||||
public static final int DEFAULT_MAX_PIPELINES = 256;
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
@@ -35,15 +36,13 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
|
||||
protected final boolean tcp; //是否TCP协议
|
||||
|
||||
protected final SocketAddress address; //连接的地址
|
||||
|
||||
protected final Creator<ClientCodec<R, P>> codecCreator;
|
||||
protected final ClientAddress address; //连接的地址
|
||||
|
||||
protected final ScheduledThreadPoolExecutor timeoutScheduler;
|
||||
|
||||
protected final LongAdder writeReqCounter = new LongAdder();
|
||||
protected final LongAdder reqWritedCounter = new LongAdder();
|
||||
|
||||
protected final LongAdder pollRespCounter = new LongAdder();
|
||||
protected final LongAdder respDoneCounter = new LongAdder();
|
||||
|
||||
private final AtomicInteger connSeqno = new AtomicInteger();
|
||||
|
||||
@@ -53,15 +52,15 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
|
||||
protected ClientConnection<R, P>[] connArray; //连接池
|
||||
|
||||
protected LongAdder[] connResps; //连接当前处理数
|
||||
protected LongAdder[] connRespWaitings; //连接当前处理数
|
||||
|
||||
protected AtomicBoolean[] connOpens; //conns的标记组,当conn不存在或closed状态,标记为false
|
||||
protected AtomicBoolean[] connOpenStates; //conns的标记组,当conn不存在或closed状态,标记为false
|
||||
|
||||
protected final Queue<CompletableFuture<ClientConnection>>[] connWaits; //连接等待池
|
||||
protected final Queue<CompletableFuture<ClientConnection>>[] connAcquireWaitings; //连接等待池
|
||||
|
||||
protected int connLimit = Utility.cpus(); //最大连接数
|
||||
|
||||
protected int maxPipelines = 16; //单个连接最大并行处理数
|
||||
protected int maxPipelines = DEFAULT_MAX_PIPELINES; //单个连接最大并行处理数
|
||||
|
||||
protected int readTimeoutSeconds;
|
||||
|
||||
@@ -79,36 +78,35 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
//创建连接后进行的登录鉴权操作
|
||||
protected Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate;
|
||||
|
||||
protected Client(AsyncGroup group, SocketAddress address, Creator<ClientCodec<R, P>> responseCreator) {
|
||||
this(group, true, address, Utility.cpus(), 16, responseCreator, null, null, null);
|
||||
protected Client(AsyncGroup group, ClientAddress address) {
|
||||
this(group, true, address, Utility.cpus(), 16, null, null, null);
|
||||
}
|
||||
|
||||
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, Creator<ClientCodec<R, P>> codecCreator) {
|
||||
this(group, tcp, address, Utility.cpus(), 16, codecCreator, null, null, null);
|
||||
protected Client(AsyncGroup group, boolean tcp, ClientAddress address) {
|
||||
this(group, tcp, address, Utility.cpus(), 16, null, null, null);
|
||||
}
|
||||
|
||||
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator) {
|
||||
this(group, tcp, address, maxconns, 16, codecCreator, null, null, null);
|
||||
protected Client(AsyncGroup group, boolean tcp, ClientAddress address, int maxconns) {
|
||||
this(group, tcp, address, maxconns, 16, null, null, null);
|
||||
}
|
||||
|
||||
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, int maxPipelines, Creator<ClientCodec<R, P>> codecCreator) {
|
||||
this(group, tcp, address, maxconns, maxPipelines, codecCreator, null, null, null);
|
||||
protected Client(AsyncGroup group, boolean tcp, ClientAddress address, int maxconns, int maxPipelines) {
|
||||
this(group, tcp, address, maxconns, maxPipelines, null, null, null);
|
||||
}
|
||||
|
||||
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator,
|
||||
protected Client(AsyncGroup group, boolean tcp, ClientAddress address, int maxconns,
|
||||
Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
|
||||
this(group, tcp, address, maxconns, 16, codecCreator, null, null, authenticate);
|
||||
this(group, tcp, address, maxconns, 16, null, null, authenticate);
|
||||
}
|
||||
|
||||
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator,
|
||||
protected Client(AsyncGroup group, boolean tcp, ClientAddress address, int maxconns,
|
||||
R closeRequest, Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
|
||||
this(group, tcp, address, maxconns, 16, codecCreator, null, closeRequest, authenticate);
|
||||
this(group, tcp, address, maxconns, 16, null, closeRequest, authenticate);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OverridableMethodCallInConstructor")
|
||||
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns,
|
||||
int maxPipelines, Creator<ClientCodec<R, P>> codecCreator, R pingRequest, R closeRequest,
|
||||
Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
|
||||
protected Client(AsyncGroup group, boolean tcp, ClientAddress address, int maxconns,
|
||||
int maxPipelines, R pingRequest, R closeRequest, Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
|
||||
if (maxPipelines < 1) throw new IllegalArgumentException("maxPipelines must bigger 0");
|
||||
this.group = group;
|
||||
this.tcp = tcp;
|
||||
@@ -117,16 +115,15 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
this.maxPipelines = maxPipelines;
|
||||
this.pingRequest = pingRequest;
|
||||
this.closeRequest = closeRequest;
|
||||
this.codecCreator = codecCreator;
|
||||
this.authenticate = authenticate;
|
||||
this.connArray = new ClientConnection[connLimit];
|
||||
this.connOpens = new AtomicBoolean[connLimit];
|
||||
this.connResps = new LongAdder[connLimit];
|
||||
this.connWaits = new Queue[connLimit];
|
||||
for (int i = 0; i < connOpens.length; i++) {
|
||||
this.connOpens[i] = new AtomicBoolean();
|
||||
this.connResps[i] = new LongAdder();
|
||||
this.connWaits[i] = Utility.unsafe() != null ? new MpscGrowableArrayQueue<>(16, 1 << 10) : new ConcurrentLinkedDeque();
|
||||
this.connOpenStates = new AtomicBoolean[connLimit];
|
||||
this.connRespWaitings = new LongAdder[connLimit];
|
||||
this.connAcquireWaitings = new Queue[connLimit];
|
||||
for (int i = 0; i < connOpenStates.length; i++) {
|
||||
this.connOpenStates[i] = new AtomicBoolean();
|
||||
this.connRespWaitings[i] = new LongAdder();
|
||||
this.connAcquireWaitings[i] = Utility.unsafe() != null ? new MpscGrowableArrayQueue<>(16, 1 << 10) : new ConcurrentLinkedDeque();
|
||||
}
|
||||
//timeoutScheduler 不仅仅给超时用, 还给write用
|
||||
this.timeoutScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
@@ -151,11 +148,13 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}, pingInterval(), pingInterval(), TimeUnit.SECONDS);
|
||||
}, pingIntervalSeconds(), pingIntervalSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
protected int pingInterval() {
|
||||
protected abstract ClientConnection createClientConnection(final int index, AsyncConnection channel);
|
||||
|
||||
protected int pingIntervalSeconds() {
|
||||
return 30;
|
||||
}
|
||||
|
||||
@@ -205,80 +204,84 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
if (cflag) {
|
||||
ClientConnection cc = context.getAttribute(connectionContextName);
|
||||
if (cc != null && cc.isOpen()) return CompletableFuture.completedFuture(cc);
|
||||
|
||||
}
|
||||
int connIndex = -1;
|
||||
int connIndex;
|
||||
final int size = this.connArray.length;
|
||||
WorkThread workThread = WorkThread.currWorkThread();
|
||||
if (workThread != null && workThread.threads() == size) {
|
||||
connIndex = workThread.index();
|
||||
} else {
|
||||
connIndex = (int) Math.abs(Thread.currentThread().getId() % size);
|
||||
}
|
||||
if (connIndex >= 0) {
|
||||
ClientConnection cc = this.connArray[connIndex];
|
||||
if (cc != null && cc.isOpen()) {
|
||||
if (cflag) context.setAttribute(connectionContextName, cc);
|
||||
return CompletableFuture.completedFuture(cc);
|
||||
}
|
||||
final int index = connIndex;
|
||||
final Queue<CompletableFuture<ClientConnection>> waitQueue = this.connWaits[index];
|
||||
if (this.connOpens[index].compareAndSet(false, true)) {
|
||||
CompletableFuture<ClientConnection> future = group.createClient(tcp, address, readTimeoutSeconds, writeTimeoutSeconds)
|
||||
.thenApply(c -> createClientConnection(index, c).setMaxPipelines(maxPipelines));
|
||||
return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> {
|
||||
c.authenticated = true;
|
||||
this.connArray[index] = c;
|
||||
CompletableFuture<ClientConnection> f;
|
||||
if (cflag) context.setAttribute(connectionContextName, c);
|
||||
while ((f = waitQueue.poll()) != null) {
|
||||
f.complete(c);
|
||||
}
|
||||
return c;
|
||||
}).whenComplete((r, t) -> {
|
||||
if (t != null) this.connOpens[index].set(false);
|
||||
});
|
||||
} else {
|
||||
CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS);
|
||||
waitQueue.offer(rs);
|
||||
return rs;
|
||||
}
|
||||
// if (connIndex >= 0) {
|
||||
ClientConnection cc = this.connArray[connIndex];
|
||||
if (cc != null && cc.isOpen()) {
|
||||
if (cflag) context.setAttribute(connectionContextName, cc);
|
||||
return CompletableFuture.completedFuture(cc);
|
||||
}
|
||||
ClientConnection minRunningConn = null;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int index = i;
|
||||
final ClientConnection conn = this.connArray[index];
|
||||
if (conn == null || !conn.isOpen()) {
|
||||
if (this.connOpens[index].compareAndSet(false, true)) {
|
||||
CompletableFuture<ClientConnection> future = group.createClient(tcp, address, readTimeoutSeconds, writeTimeoutSeconds)
|
||||
.thenApply(c -> createClientConnection(index, c).setMaxPipelines(maxPipelines));
|
||||
return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> {
|
||||
c.authenticated = true;
|
||||
this.connArray[index] = c;
|
||||
return c;
|
||||
}).whenComplete((r, t) -> {
|
||||
if (t != null) this.connOpens[index].set(false);
|
||||
});
|
||||
final int index = connIndex;
|
||||
final Queue<CompletableFuture<ClientConnection>> waitQueue = this.connAcquireWaitings[index];
|
||||
if (this.connOpenStates[index].compareAndSet(false, true)) {
|
||||
CompletableFuture<ClientConnection> future = address.createClient(tcp, group, readTimeoutSeconds, writeTimeoutSeconds)
|
||||
.thenApply(c -> createClientConnection(index, c).setMaxPipelines(maxPipelines));
|
||||
return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> {
|
||||
c.authenticated = true;
|
||||
this.connArray[index] = c;
|
||||
CompletableFuture<ClientConnection> f;
|
||||
if (cflag) context.setAttribute(connectionContextName, c);
|
||||
while ((f = waitQueue.poll()) != null) {
|
||||
f.complete(c);
|
||||
}
|
||||
} else if (conn.runningCount() < 1) {
|
||||
return CompletableFuture.completedFuture(conn);
|
||||
} else if (minRunningConn == null || minRunningConn.runningCount() > conn.runningCount()) {
|
||||
minRunningConn = conn;
|
||||
}
|
||||
return c;
|
||||
}).whenComplete((r, t) -> {
|
||||
if (t != null) this.connOpenStates[index].set(false);
|
||||
});
|
||||
} else {
|
||||
CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS);
|
||||
waitQueue.offer(rs);
|
||||
return rs;
|
||||
}
|
||||
if (minRunningConn != null && minRunningConn.runningCount() < maxPipelines) {
|
||||
ClientConnection minConn = minRunningConn;
|
||||
return CompletableFuture.completedFuture(minConn);
|
||||
}
|
||||
return waitClientConnection();
|
||||
// }
|
||||
// ClientConnection minRunningConn = null;
|
||||
// for (int i = 0; i < size; i++) {
|
||||
// final int index = i;
|
||||
// final ClientConnection conn = this.connArray[index];
|
||||
// if (conn == null || !conn.isOpen()) {
|
||||
// if (this.connOpenStates[index].compareAndSet(false, true)) {
|
||||
// CompletableFuture<ClientConnection> future = group.createClient(tcp, address, readTimeoutSeconds, writeTimeoutSeconds)
|
||||
// .thenApply(c -> createClientConnection(index, c).setMaxPipelines(maxPipelines));
|
||||
// return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> {
|
||||
// c.authenticated = true;
|
||||
// this.connArray[index] = c;
|
||||
// return c;
|
||||
// }).whenComplete((r, t) -> {
|
||||
// if (t != null) this.connOpenStates[index].set(false);
|
||||
// });
|
||||
// }
|
||||
// } else if (conn.runningCount() < 1) {
|
||||
// return CompletableFuture.completedFuture(conn);
|
||||
// } else if (minRunningConn == null || minRunningConn.runningCount() > conn.runningCount()) {
|
||||
// minRunningConn = conn;
|
||||
// }
|
||||
// }
|
||||
// if (minRunningConn != null) { // && minRunningConn.runningCount() < maxPipelines
|
||||
// return CompletableFuture.completedFuture(minRunningConn);
|
||||
// }
|
||||
// return waitClientConnection();
|
||||
}
|
||||
|
||||
protected CompletableFuture<ClientConnection> waitClientConnection() {
|
||||
CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS);
|
||||
connWaits[connSeqno.getAndIncrement() % this.connLimit].offer(rs);
|
||||
connAcquireWaitings[connSeqno.getAndIncrement() % this.connLimit].offer(rs);
|
||||
return rs;
|
||||
}
|
||||
|
||||
protected ClientConnection createClientConnection(final int index, AsyncConnection channel) {
|
||||
return new ClientConnection(this, index, channel);
|
||||
protected long getRespWaitingCount() {
|
||||
long s = 0;
|
||||
for (LongAdder a : connRespWaitings) {
|
||||
s += a.longValue();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public int getReadTimeoutSeconds() {
|
||||
|
||||
48
src/main/java/org/redkale/net/client/ClientAddress.java
Normal file
48
src/main/java/org/redkale/net/client/ClientAddress.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.net.client;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.*;
|
||||
|
||||
/**
|
||||
* Client连接地址
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class ClientAddress implements java.io.Serializable {
|
||||
|
||||
protected SocketAddress address;
|
||||
|
||||
public ClientAddress() {
|
||||
}
|
||||
|
||||
|
||||
public ClientAddress(SocketAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public CompletableFuture<AsyncConnection> createClient(final boolean tcp, final AsyncGroup group, int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
return group.createClient(tcp, address, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
public SocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(SocketAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,14 +25,17 @@ public abstract class ClientCodec<R extends ClientRequest, P> {
|
||||
|
||||
protected final List<ClientResult<P>> results = new ArrayList<>();
|
||||
|
||||
public ClientCodec() {
|
||||
protected final ClientConnection connection;
|
||||
|
||||
public ClientCodec(ClientConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
//返回true: array会clear, 返回false: buffer会clear
|
||||
public abstract boolean codecResult(ClientConnection conn, ByteBuffer buffer, ByteArray array);
|
||||
public abstract boolean codecResult(ByteBuffer buffer, ByteArray array);
|
||||
|
||||
protected Queue<ClientFuture> responseQueue(ClientConnection conn) {
|
||||
return conn.responseQueue;
|
||||
protected Queue<ClientFuture> responseQueue() {
|
||||
return connection.responseQueue;
|
||||
}
|
||||
|
||||
public List<ClientResult<P>> removeResults() {
|
||||
@@ -42,6 +45,10 @@ public abstract class ClientCodec<R extends ClientRequest, P> {
|
||||
return rs;
|
||||
}
|
||||
|
||||
public ClientConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void addResult(P result) {
|
||||
this.results.add(new ClientResult<>(result));
|
||||
}
|
||||
|
||||
@@ -27,13 +27,15 @@ import org.redkale.util.*;
|
||||
* @param <R> 请求对象
|
||||
* @param <P> 响应对象
|
||||
*/
|
||||
public class ClientConnection<R extends ClientRequest, P> implements Consumer<AsyncConnection> {
|
||||
public abstract class ClientConnection<R extends ClientRequest, P> implements Consumer<AsyncConnection> {
|
||||
|
||||
protected final int index; //从0开始, connArray的下坐标
|
||||
|
||||
protected final Client<R, P> client;
|
||||
|
||||
protected final LongAdder respCounter;
|
||||
protected final ClientCodec<R, P> codec;
|
||||
|
||||
protected final LongAdder respWaitingCounter;
|
||||
|
||||
protected final AsyncConnection channel;
|
||||
|
||||
@@ -49,7 +51,7 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
|
||||
protected final Queue<R> requestQueue = new ArrayDeque<>();
|
||||
|
||||
protected final Queue<ClientFuture> responseQueue = new ArrayDeque<>();
|
||||
protected final ArrayDeque<ClientFuture> responseQueue = new ArrayDeque<>();
|
||||
|
||||
protected final CompletionHandler<Integer, Void> writeHandler = new CompletionHandler<Integer, Void>() {
|
||||
|
||||
@@ -73,6 +75,8 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
|
||||
protected int maxPipelines; //最大并行处理数
|
||||
|
||||
protected R lastHalfRequest;
|
||||
|
||||
protected ClientConnection setMaxPipelines(int maxPipelines) {
|
||||
this.maxPipelines = maxPipelines;
|
||||
return this;
|
||||
@@ -83,28 +87,56 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void pauseWriting(boolean flag) {
|
||||
this.pauseWriting.set(flag);
|
||||
protected void resumeWrite() {
|
||||
this.pauseWriting.set(false);
|
||||
}
|
||||
|
||||
private boolean continueWrite(boolean must) {
|
||||
writeArray.clear();
|
||||
ClientConnection conn = this;
|
||||
ByteArray rw = conn.writeArray;
|
||||
rw.clear();
|
||||
int pipelines = maxPipelines > 1 ? (maxPipelines - responseQueue.size()) : 1;
|
||||
if (must && pipelines < 1) pipelines = 1;
|
||||
int c = 0;
|
||||
AtomicBoolean pw = this.pauseWriting;
|
||||
AtomicBoolean pw = conn.pauseWriting;
|
||||
for (int i = 0; i < pipelines; i++) {
|
||||
if (pw.get()) break;
|
||||
R r = requestQueue.poll();
|
||||
if (r == null) break;
|
||||
writeLastRequest = r;
|
||||
r.accept(this, writeArray);
|
||||
R req;
|
||||
if (lastHalfRequest == null) {
|
||||
req = requestQueue.poll();
|
||||
} else {
|
||||
req = lastHalfRequest;
|
||||
lastHalfRequest = null;
|
||||
}
|
||||
if (req == null) break;
|
||||
writeLastRequest = req;
|
||||
if (req.canMerge(conn)) {
|
||||
R r;
|
||||
while ((r = requestQueue.poll()) != null) {
|
||||
i++;
|
||||
if (!req.merge(conn, r)) break;
|
||||
req.respFuture.mergeCount++;
|
||||
}
|
||||
req.accept(conn, rw);
|
||||
if (r != null) {
|
||||
r.accept(conn, rw);
|
||||
req = r;
|
||||
}
|
||||
} else {
|
||||
req.accept(conn, rw);
|
||||
}
|
||||
c++;
|
||||
if (!req.isCompleted()) {
|
||||
lastHalfRequest = req;
|
||||
this.pauseWriting.set(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c > 0) { //当Client连接Server时先从Server读取数据时,会先发送一个EMPTY的request,这样writeArray.count就会为0
|
||||
channel.write(writeArray, writeHandler);
|
||||
if (c > 0) { //当Client连接Server后先从Server读取数据时,会先发送一个EMPTY的request,这样writeArray.count就会为0
|
||||
channel.write(rw, writeHandler);
|
||||
return true;
|
||||
}
|
||||
if (pw.get()) writePending.compareAndSet(true, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,8 +145,6 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
|
||||
protected final CompletionHandler<Integer, ByteBuffer> readHandler = new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
ClientCodec<R, P> codec;
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, ByteBuffer attachment) {
|
||||
if (count < 1) {
|
||||
@@ -123,7 +153,6 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (codec == null) codec = client.codecCreator.create();
|
||||
attachment.flip();
|
||||
codecResponse(attachment);
|
||||
} catch (Throwable e) {
|
||||
@@ -132,43 +161,66 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
}
|
||||
}
|
||||
|
||||
protected void completeResponse(ClientResult<P> rs, ClientFuture respFuture) {
|
||||
if (respFuture != null) {
|
||||
if (!respFuture.request.isCompleted()) {
|
||||
if (rs.exc == null) {
|
||||
responseQueue.offerFirst(respFuture);
|
||||
pauseWriting.set(false);
|
||||
return;
|
||||
} else { //异常了需要清掉半包
|
||||
lastHalfRequest = null;
|
||||
pauseWriting.set(false);
|
||||
}
|
||||
}
|
||||
respWaitingCounter.decrement();
|
||||
if (isAuthenticated() && client.respDoneCounter != null) client.respDoneCounter.increment();
|
||||
try {
|
||||
if (respFuture.timeout != null) respFuture.timeout.cancel(true);
|
||||
ClientRequest request = respFuture.request;
|
||||
//if (client.finest) client.logger.log(Level.FINEST, Utility.nowMillis() + ": " + Thread.currentThread().getName() + ": " + ClientConnection.this + ", 回调处理, req=" + request + ", result=" + rs.result);
|
||||
preComplete(rs.result, (R) request, rs.exc);
|
||||
WorkThread workThread = null;
|
||||
if (request != null) {
|
||||
workThread = request.workThread;
|
||||
request.workThread = null;
|
||||
}
|
||||
if (rs.exc != null) {
|
||||
if (workThread == null || workThread == Thread.currentThread() || workThread.inIO()
|
||||
|| workThread.getState() != Thread.State.RUNNABLE) {
|
||||
respFuture.completeExceptionally(rs.exc);
|
||||
} else {
|
||||
workThread.execute(() -> respFuture.completeExceptionally(rs.exc));
|
||||
}
|
||||
} else {
|
||||
if (workThread == null || workThread == Thread.currentThread() || workThread.inIO()
|
||||
|| workThread.getState() != Thread.State.RUNNABLE) {
|
||||
respFuture.complete(rs.result);
|
||||
} else {
|
||||
workThread.execute(() -> respFuture.complete(rs.result));
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
client.logger.log(Level.INFO, "complete result error, request: " + respFuture.request, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void codecResponse(ByteBuffer buffer) {
|
||||
if (codec.codecResult(ClientConnection.this, buffer, readArray)) { //成功了
|
||||
if (codec.codecResult(buffer, readArray)) { //成功了
|
||||
readArray.clear();
|
||||
List<ClientResult<P>> results = codec.removeResults();
|
||||
if (results != null) {
|
||||
for (ClientResult<P> rs : results) {
|
||||
ClientFuture respFuture = responseQueue.poll();
|
||||
if (respFuture != null) {
|
||||
respCounter.decrement();
|
||||
if (isAuthenticated() && client.pollRespCounter != null) client.pollRespCounter.increment();
|
||||
try {
|
||||
if (respFuture.timeout != null) respFuture.timeout.cancel(true);
|
||||
ClientRequest request = respFuture.request;
|
||||
//if (client.finest) client.logger.log(Level.FINEST, Utility.nowMillis() + ": " + Thread.currentThread().getName() + ": " + ClientConnection.this + ", 回调处理, req=" + request + ", result=" + rs.result);
|
||||
preComplete(rs.result, (R) request, rs.exc);
|
||||
WorkThread workThread = null;
|
||||
if (request != null) {
|
||||
workThread = request.workThread;
|
||||
request.workThread = null;
|
||||
int mergeCount = respFuture.mergeCount;
|
||||
completeResponse(rs, respFuture);
|
||||
if (mergeCount > 0) {
|
||||
for (int i = 0; i < mergeCount; i++) {
|
||||
respFuture = responseQueue.poll();
|
||||
if (respFuture != null) completeResponse(rs, respFuture);
|
||||
}
|
||||
if (rs.exc != null) {
|
||||
if (workThread == null || workThread == Thread.currentThread() || workThread.inIO()
|
||||
|| workThread.getState() != Thread.State.RUNNABLE) {
|
||||
respFuture.completeExceptionally(rs.exc);
|
||||
} else {
|
||||
workThread.execute(() -> respFuture.completeExceptionally(rs.exc));
|
||||
}
|
||||
} else {
|
||||
if (workThread == null || workThread == Thread.currentThread() || workThread.inIO()
|
||||
|| workThread.getState() != Thread.State.RUNNABLE) {
|
||||
respFuture.complete(rs.result);
|
||||
} else {
|
||||
workThread.execute(() -> respFuture.complete(rs.result));
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
client.logger.log(Level.INFO, "complete result error, request: " + respFuture.request, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +237,7 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
channel.read(this);
|
||||
}
|
||||
} else { //还有消息需要读取
|
||||
if (!requestQueue.isEmpty() && writePending.compareAndSet(false, true)) {
|
||||
if ((!requestQueue.isEmpty() || lastHalfRequest != null) && writePending.compareAndSet(false, true)) {
|
||||
//先写后读取
|
||||
if (!continueWrite(true)) {
|
||||
writePending.compareAndSet(true, false);
|
||||
@@ -214,14 +266,17 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
|
||||
private R writeLastRequest;
|
||||
|
||||
@SuppressWarnings("LeakingThisInConstructor")
|
||||
@SuppressWarnings({"LeakingThisInConstructor", "OverridableMethodCallInConstructor"})
|
||||
public ClientConnection(Client client, int index, AsyncConnection channel) {
|
||||
this.client = client;
|
||||
this.codec = createCodec();
|
||||
this.index = index;
|
||||
this.respCounter = client.connResps[index];
|
||||
this.respWaitingCounter = client.connRespWaitings[index];
|
||||
this.channel = channel.beforeCloseListener(this);
|
||||
}
|
||||
|
||||
protected abstract ClientCodec createCodec();
|
||||
|
||||
protected CompletableFuture<P> writeChannel(R request) {
|
||||
ClientFuture respFuture = createClientFuture(request);
|
||||
if (request == client.closeRequest) {
|
||||
@@ -230,10 +285,11 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
} else {
|
||||
int rts = this.channel.getReadTimeoutSeconds();
|
||||
if (rts > 0 && respFuture.request != null) {
|
||||
respFuture.responseQueue = responseQueue;
|
||||
respFuture.conn = this;
|
||||
respFuture.timeout = client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
respWaitingCounter.increment(); //放在writeChannelInThread计数会延迟,导致不准确
|
||||
if (channel.inCurrThread()) {
|
||||
writeChannelInThread(request, respFuture);
|
||||
} else {
|
||||
@@ -244,12 +300,16 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
|
||||
private void writeChannelInThread(R request, ClientFuture respFuture) {
|
||||
{ //保证顺序一致
|
||||
responseQueue.offer(client.closeRequest != null && respFuture.request == client.closeRequest ? ClientFuture.EMPTY : respFuture);
|
||||
if (client.closeRequest != null && respFuture.request == client.closeRequest) {
|
||||
responseQueue.offer(ClientFuture.EMPTY);
|
||||
} else {
|
||||
request.respFuture = respFuture;
|
||||
responseQueue.offer(respFuture);
|
||||
}
|
||||
requestQueue.offer(request);
|
||||
respCounter.increment();
|
||||
if (isAuthenticated() && client.writeReqCounter != null) client.writeReqCounter.increment();
|
||||
if (isAuthenticated() && client.reqWritedCounter != null) client.reqWritedCounter.increment();
|
||||
}
|
||||
if (writePending.compareAndSet(false, true)) {
|
||||
if (responseQueue.size() < 2 && writePending.compareAndSet(false, true)) {//responseQueue.size() < 2 && 加了这句会存在偶尔不写数据的问题?
|
||||
continueWrite(true);
|
||||
}
|
||||
}
|
||||
@@ -273,10 +333,14 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
return channel;
|
||||
}
|
||||
|
||||
public ClientCodec<R, P> getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
@Override //AsyncConnection.beforeCloseListener
|
||||
public void accept(AsyncConnection t) {
|
||||
respCounter.reset();
|
||||
client.connOpens[index].set(false);
|
||||
respWaitingCounter.reset();
|
||||
client.connOpenStates[index].set(false);
|
||||
client.connArray[index] = null; //必须connflags之后
|
||||
}
|
||||
|
||||
@@ -284,7 +348,7 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
channel.dispose();
|
||||
Throwable e = exc;
|
||||
CompletableFuture f;
|
||||
respCounter.reset();
|
||||
respWaitingCounter.reset();
|
||||
while ((f = responseQueue.poll()) != null) {
|
||||
if (e == null) e = new ClosedChannelException();
|
||||
f.completeExceptionally(e);
|
||||
@@ -292,7 +356,7 @@ public class ClientConnection<R extends ClientRequest, P> implements Consumer<As
|
||||
}
|
||||
|
||||
public int runningCount() {
|
||||
return respCounter.intValue();
|
||||
return respWaitingCounter.intValue();
|
||||
}
|
||||
|
||||
public long getLastWriteTime() {
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.redkale.net.client;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.*;
|
||||
import org.redkale.net.WorkThread;
|
||||
import org.redkale.net.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -32,7 +32,9 @@ public class ClientFuture<T> extends CompletableFuture<T> implements Runnable {
|
||||
|
||||
ScheduledFuture timeout;
|
||||
|
||||
Queue<ClientFuture> responseQueue;
|
||||
int mergeCount; //合并的个数,不算自身
|
||||
|
||||
ClientConnection conn;
|
||||
|
||||
public ClientFuture() {
|
||||
super();
|
||||
@@ -43,6 +45,10 @@ public class ClientFuture<T> extends CompletableFuture<T> implements Runnable {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public int getMergeCount() {
|
||||
return mergeCount;
|
||||
}
|
||||
|
||||
@Override //JDK9+
|
||||
public <U> ClientFuture<U> newIncompleteFuture() {
|
||||
return new ClientFuture<>();
|
||||
@@ -54,6 +60,17 @@ public class ClientFuture<T> extends CompletableFuture<T> implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (conn == null) return;
|
||||
AsyncConnection channel = conn.getChannel();
|
||||
if (channel.inCurrThread()) {
|
||||
this.runTimeout();
|
||||
} else {
|
||||
channel.execute(this::runTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
private void runTimeout() {
|
||||
Queue<ClientFuture> responseQueue = conn.responseQueue;
|
||||
if (responseQueue != null) responseQueue.remove(this);
|
||||
TimeoutException ex = new TimeoutException();
|
||||
WorkThread workThread = null;
|
||||
|
||||
@@ -22,7 +22,9 @@ public abstract class ClientRequest implements BiConsumer<ClientConnection, Byte
|
||||
|
||||
protected long createTime = System.currentTimeMillis();
|
||||
|
||||
WorkThread workThread;
|
||||
protected WorkThread workThread;
|
||||
|
||||
ClientFuture respFuture;
|
||||
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
@@ -33,6 +35,25 @@ public abstract class ClientRequest implements BiConsumer<ClientConnection, Byte
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public int getMergeCount() {
|
||||
return respFuture == null ? -1 : respFuture.mergeCount;
|
||||
}
|
||||
|
||||
//是否能合并
|
||||
protected boolean canMerge(ClientConnection conn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//合并成功了返回true
|
||||
protected boolean merge(ClientConnection conn, ClientRequest other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//数据是否全部写入,如果只写部分,返回false
|
||||
protected boolean isCompleted() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void prepare() {
|
||||
this.createTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.net.client;
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <P> result
|
||||
*/
|
||||
public class ClientResult<P> {
|
||||
|
||||
|
||||
@@ -30,12 +30,18 @@ public class HttpContext extends Context {
|
||||
|
||||
protected final String remoteAddrHeader;
|
||||
|
||||
protected final HttpRpcAuthenticator rpcAuthenticator;
|
||||
|
||||
protected final AnyValue rpcAuthenticatorConfig;
|
||||
|
||||
protected boolean lazyHeaders; //存在动态改值
|
||||
|
||||
// protected RequestURINode[] uriCacheNodes;
|
||||
public HttpContext(HttpContextConfig config) {
|
||||
super(config);
|
||||
this.remoteAddrHeader = config.remoteAddrHeader;
|
||||
this.rpcAuthenticator = config.rpcAuthenticator;
|
||||
this.rpcAuthenticatorConfig = config.rpcAuthenticatorConfig;
|
||||
random.setSeed(Math.abs(System.nanoTime()));
|
||||
}
|
||||
|
||||
@@ -184,6 +190,10 @@ public class HttpContext extends Context {
|
||||
|
||||
public String remoteAddrHeader;
|
||||
|
||||
public HttpRpcAuthenticator rpcAuthenticator;
|
||||
|
||||
public AnyValue rpcAuthenticatorConfig;
|
||||
|
||||
}
|
||||
|
||||
protected static class RequestURINode {
|
||||
|
||||
@@ -146,6 +146,8 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
|
||||
private final String remoteAddrHeader;
|
||||
|
||||
final HttpRpcAuthenticator rpcAuthenticator;
|
||||
|
||||
HttpServlet.ActionEntry actionEntry; //仅供HttpServlet传递Entry使用
|
||||
|
||||
public HttpRequest(HttpContext context) {
|
||||
@@ -156,6 +158,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
super(context);
|
||||
this.array = array;
|
||||
this.remoteAddrHeader = context.remoteAddrHeader;
|
||||
this.rpcAuthenticator = context.rpcAuthenticator;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OverridableMethodCallInConstructor")
|
||||
@@ -163,6 +166,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
super(context);
|
||||
this.array = new ByteArray();
|
||||
this.remoteAddrHeader = null;
|
||||
this.rpcAuthenticator = null;
|
||||
if (req != null) initSimpleRequest(req, true);
|
||||
}
|
||||
|
||||
@@ -198,10 +202,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
HttpSimpleRequest req = new HttpSimpleRequest();
|
||||
req.setBody(array.length() == 0 ? null : array.getBytes());
|
||||
if (!getHeaders().isEmpty()) {
|
||||
if (headers.containsKey(Rest.REST_HEADER_RPC_NAME)
|
||||
if (headers.containsKey(Rest.REST_HEADER_RPC)
|
||||
|| headers.containsKey(Rest.REST_HEADER_CURRUSERID_NAME)) { //外部request不能包含RPC的header信息
|
||||
req.setHeaders(new HashMap<>(headers));
|
||||
req.removeHeader(Rest.REST_HEADER_RPC_NAME);
|
||||
req.removeHeader(Rest.REST_HEADER_RPC);
|
||||
req.removeHeader(Rest.REST_HEADER_CURRUSERID_NAME);
|
||||
} else {
|
||||
req.setHeaders(headers);
|
||||
@@ -655,7 +659,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
value = bytes.toString(charset);
|
||||
headers.put("User-Agent", value);
|
||||
break;
|
||||
case Rest.REST_HEADER_RPC_NAME:
|
||||
case Rest.REST_HEADER_RPC:
|
||||
value = bytes.toString(charset);
|
||||
this.rpc = "true".equalsIgnoreCase(value);
|
||||
headers.put(name, value);
|
||||
@@ -827,7 +831,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return;
|
||||
}
|
||||
String name = toDecodeString(array, offset, keypos - offset, charset);
|
||||
if (name.charAt(0) == '<') return; //内容可能是xml格式; 如: <?xml version="1.0"
|
||||
if (!name.isEmpty() && name.charAt(0) == '<') return; //内容可能是xml格式; 如: <?xml version="1.0"
|
||||
++keypos;
|
||||
String value = toDecodeString(array, keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset);
|
||||
this.params.put(name, value);
|
||||
@@ -926,6 +930,21 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return uid.isEmpty() ? 0 : Integer.parseInt(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户ID的long值<br>
|
||||
*
|
||||
* @return 用户ID
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public long currentLongUserid() {
|
||||
if (currentUserid == CURRUSERID_NIL || currentUserid == null) return 0L;
|
||||
if (this.currentUserid instanceof Number) return ((Number) this.currentUserid).longValue();
|
||||
String uid = this.currentUserid.toString();
|
||||
return uid.isEmpty() ? 0L : Long.parseLong(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户ID<br>
|
||||
*
|
||||
@@ -939,16 +958,16 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Serializable> T currentUserid(Class<T> type) {
|
||||
if (currentUserid == CURRUSERID_NIL || currentUserid == null) {
|
||||
if (type == int.class) return (T) (Integer) (int) 0;
|
||||
if (type == long.class) return (T) (Long) (long) 0;
|
||||
if (type == int.class || type == Integer.class) return (T) (Integer) (int) 0;
|
||||
if (type == long.class || type == Long.class) return (T) (Long) (long) 0;
|
||||
return null;
|
||||
}
|
||||
if (type == int.class) {
|
||||
if (type == int.class || type == Integer.class) {
|
||||
if (this.currentUserid instanceof Number) return (T) (Integer) ((Number) this.currentUserid).intValue();
|
||||
String uid = this.currentUserid.toString();
|
||||
return (T) (Integer) (uid.isEmpty() ? 0 : Integer.parseInt(uid));
|
||||
}
|
||||
if (type == long.class) {
|
||||
if (type == long.class || type == Long.class) {
|
||||
if (this.currentUserid instanceof Number) return (T) (Long) ((Number) this.currentUserid).longValue();
|
||||
String uid = this.currentUserid.toString();
|
||||
return (T) (Long) (uid.isEmpty() ? 0L : Long.parseLong(uid));
|
||||
@@ -1182,9 +1201,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
|
||||
private static CharSequence toMapString(Map<String, String> map, int indent) {
|
||||
char[] chars = new char[indent];
|
||||
Arrays.fill(chars, ' ');
|
||||
final String space = new String(chars);
|
||||
final String space = " ".repeat(indent);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{\r\n");
|
||||
for (Map.Entry en : map.entrySet()) {
|
||||
|
||||
@@ -36,6 +36,8 @@ import static org.redkale.util.Utility.append;
|
||||
*/
|
||||
public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
protected static final byte[] EMPTY_BTYES = new byte[0];
|
||||
|
||||
protected static final byte[] bytes304 = "HTTP/1.1 304 Not Modified\r\nContent-Length:0\r\n\r\n".getBytes();
|
||||
|
||||
protected static final byte[] bytes404 = "HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes();
|
||||
@@ -451,7 +453,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
// convert.convertToBytes(type, ret, convertHandler);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 将RetResult对象以JSON格式输出
|
||||
*
|
||||
@@ -478,7 +479,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
// convert.convertToBytes(type, ret, convertHandler);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 将CompletableFuture的结果对象以JSON格式输出
|
||||
*
|
||||
@@ -491,7 +491,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
// public void finishJson(final JsonConvert convert, final Type valueType, final CompletableFuture future) {
|
||||
// finish(convert, valueType, future);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 将RetResult对象输出
|
||||
*
|
||||
@@ -580,9 +579,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
|
||||
Object val = result.getResult();
|
||||
if (val == null) {
|
||||
finish("");
|
||||
finish((String) null, EMPTY_BTYES);
|
||||
} else if (val instanceof CharSequence) {
|
||||
finish(val.toString());
|
||||
finish(getStatus(), val.toString());
|
||||
} else {
|
||||
Convert cc = result.convert();
|
||||
if (cc == null) cc = convert;
|
||||
@@ -666,6 +665,36 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
finish(convert, valueType, (CompletionStage) Flows.maybePublisherToFuture(publisher));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpScope对象输出
|
||||
*
|
||||
* @param future HttpScope输出异步对象
|
||||
*/
|
||||
public void finishScope(CompletionStage<HttpScope> future) {
|
||||
finish(request.getRespConvert(), future);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpScope对象输出
|
||||
*
|
||||
* @param convert 指定的Convert
|
||||
* @param future HttpScope输出异步对象
|
||||
*/
|
||||
public void finishScope(final Convert convert, CompletionStage<HttpScope> future) {
|
||||
future.whenComplete((v, e) -> {
|
||||
if (e != null) {
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionStage", (Throwable) e);
|
||||
if (e instanceof TimeoutException) {
|
||||
finish504();
|
||||
} else {
|
||||
finish500();
|
||||
}
|
||||
return;
|
||||
}
|
||||
finish(convert, v);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpScope对象输出
|
||||
*
|
||||
@@ -932,12 +961,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void error() {
|
||||
finish500();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 以304状态码输出
|
||||
*/
|
||||
@@ -1297,6 +1326,17 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
return this.header.duplicate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在Header值
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean existsHeader(String name) {
|
||||
return this.header.getValue(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Header值
|
||||
*
|
||||
@@ -1549,6 +1589,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
public HttpRender httpRender;
|
||||
|
||||
public AnyValue renderConfig;
|
||||
|
||||
public final byte[][] plainLiveContentLengthArray = new byte[cacheMaxContentLength][];
|
||||
|
||||
public final byte[][] jsonLiveContentLengthArray = new byte[cacheMaxContentLength][];
|
||||
|
||||
44
src/main/java/org/redkale/net/http/HttpRpcAuthenticator.java
Normal file
44
src/main/java/org/redkale/net/http/HttpRpcAuthenticator.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* rpc鉴权验证器 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*
|
||||
*/
|
||||
public interface HttpRpcAuthenticator {
|
||||
|
||||
/**
|
||||
* 初始化方法
|
||||
*
|
||||
* @param config 配置参数
|
||||
*/
|
||||
default void init(AnyValue config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回true, 不成功返回false,且需要response.finish()输出失败的信息, 比如404
|
||||
*
|
||||
* @param request HttpRequest
|
||||
* @param response HttpResponse
|
||||
*
|
||||
* @return 是否验证成功
|
||||
*/
|
||||
public boolean auth(HttpRequest request, HttpResponse response);
|
||||
|
||||
/**
|
||||
* 销毁方法
|
||||
*
|
||||
* @param config 配置参数
|
||||
*/
|
||||
default void destroy(AnyValue config) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* rpc鉴权验证器Secret key的实现类 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*
|
||||
*/
|
||||
public class HttpRpcSecretAuthenticator implements HttpRpcAuthenticator {
|
||||
|
||||
protected String secretKey = "";
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) {
|
||||
this.secretKey = config.getValue("secret").trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean auth(HttpRequest request, HttpResponse response) {
|
||||
String key = request.getHeader("rest-rpc-secret");
|
||||
if (key == null) {
|
||||
response.finish(404, null);
|
||||
return false;
|
||||
}
|
||||
if (!secretKey.equals(key)) {
|
||||
response.finish(404, null);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,12 +6,15 @@
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.*;
|
||||
import javax.persistence.Transient;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.util.TypeToken;
|
||||
|
||||
/**
|
||||
* HTTP输出引擎的对象域 <br>
|
||||
@@ -41,10 +44,17 @@ public class HttpScope {
|
||||
|
||||
public static final Object NIL = new Object();
|
||||
|
||||
static final Type FUTRU_TYPE = new TypeToken<CompletableFuture<HttpScope>>() {
|
||||
}.getType();
|
||||
|
||||
@ConvertColumn(index = 1)
|
||||
protected String referid;
|
||||
|
||||
//@since 2.7.0
|
||||
@ConvertColumn(index = 2)
|
||||
protected Object referObj;
|
||||
|
||||
@ConvertColumn(index = 3)
|
||||
protected Map<String, Object> attributes;
|
||||
|
||||
//@since 2.4.0
|
||||
@@ -52,11 +62,11 @@ public class HttpScope {
|
||||
protected Function<String, Object> attrFunction;
|
||||
|
||||
//@since 2.4.0
|
||||
@ConvertColumn(index = 3)
|
||||
@ConvertColumn(index = 4)
|
||||
protected Map<String, String> headers;
|
||||
|
||||
//@since 2.4.0
|
||||
@ConvertColumn(index = 4)
|
||||
@ConvertColumn(index = 5)
|
||||
protected List<HttpCookie> cookies;
|
||||
|
||||
public static HttpScope refer(String template) {
|
||||
@@ -107,6 +117,11 @@ public class HttpScope {
|
||||
return rs;
|
||||
}
|
||||
|
||||
public HttpScope referObj(Object value) {
|
||||
this.referObj = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpScope attrFunc(Function<String, Object> attrFunction) {
|
||||
this.attrFunction = attrFunction;
|
||||
return this;
|
||||
@@ -131,7 +146,7 @@ public class HttpScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpScope attr(Map<String, Object> map) {
|
||||
public HttpScope attr(Map<String, ?> map) {
|
||||
if (map == null) return this;
|
||||
if (this.attributes == null) this.attributes = new LinkedHashMap<>();
|
||||
this.attributes.putAll(map);
|
||||
@@ -216,6 +231,14 @@ public class HttpScope {
|
||||
this.referid = referid;
|
||||
}
|
||||
|
||||
public Object getReferObj() {
|
||||
return referObj;
|
||||
}
|
||||
|
||||
public void setReferObj(Object referObj) {
|
||||
this.referObj = referObj;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAttributes() {
|
||||
final Function<String, Object> attrFunc = this.attrFunction;
|
||||
if (attrFunc != null) {
|
||||
|
||||
@@ -56,6 +56,9 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
@Override
|
||||
public void init(AnyValue config) throws Exception {
|
||||
super.init(config);
|
||||
if (context.rpcAuthenticator != null) {
|
||||
context.rpcAuthenticator.init(context.rpcAuthenticatorConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,6 +73,9 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
this.dateScheduler.shutdownNow();
|
||||
this.dateScheduler = null;
|
||||
}
|
||||
if (context.rpcAuthenticator != null) {
|
||||
context.rpcAuthenticator.destroy(context.rpcAuthenticatorConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public List<HttpServlet> getHttpServlets() {
|
||||
@@ -328,10 +334,12 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
String jsonContentType = null;
|
||||
HttpCookie defaultCookie = null;
|
||||
String remoteAddrHeader = null;
|
||||
AnyValue rpcAuthenticatorConfig = null;
|
||||
|
||||
if (config != null) {
|
||||
AnyValue reqs = config.getAnyValue("request");
|
||||
if (reqs != null) {
|
||||
rpcAuthenticatorConfig = reqs.getAnyValue("rpc");
|
||||
AnyValue raddr = reqs.getAnyValue("remoteaddr");
|
||||
remoteAddrHeader = raddr == null ? null : raddr.getValue("value");
|
||||
if (remoteAddrHeader != null) {
|
||||
@@ -457,15 +465,40 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
respConfig.autoOptions = autoOptions;
|
||||
respConfig.dateSupplier = dateSupplier;
|
||||
respConfig.httpRender = httpRender;
|
||||
respConfig.renderConfig = renderConfig;
|
||||
respConfig.init(config);
|
||||
|
||||
final HttpContextConfig contextConfig = new HttpContextConfig();
|
||||
initContextConfig(contextConfig);
|
||||
contextConfig.remoteAddrHeader = addrHeader;
|
||||
contextConfig.rpcAuthenticatorConfig = rpcAuthenticatorConfig;
|
||||
if (rpcAuthenticatorConfig != null) {
|
||||
String impl = rpcAuthenticatorConfig.getValue("authenticator", "").trim();
|
||||
if (impl.isEmpty()) {
|
||||
throw new RuntimeException("init HttpRpcAuthenticator(" + impl + ") error");
|
||||
}
|
||||
try {
|
||||
Class implClass = serverClassLoader.loadClass(impl);
|
||||
if (!HttpRpcAuthenticator.class.isAssignableFrom(implClass)) {
|
||||
throw new RuntimeException("" + impl + " not HttpRpcAuthenticator implement class");
|
||||
}
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(implClass, implClass.getName());
|
||||
contextConfig.rpcAuthenticator = (HttpRpcAuthenticator) implClass.getConstructor().newInstance();
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("init HttpRpcAuthenticator(" + impl + ") error", e);
|
||||
}
|
||||
}
|
||||
return new HttpContext(contextConfig);
|
||||
}
|
||||
|
||||
HttpContext c = new HttpContext(contextConfig);
|
||||
if (httpRender != null) httpRender.init(c, renderConfig);
|
||||
return c;
|
||||
@Override
|
||||
protected void postPrepareInit() {
|
||||
HttpRender httpRender = this.respConfig.httpRender;
|
||||
if (httpRender != null) {
|
||||
httpRender.init(context, this.respConfig.renderConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -48,9 +48,15 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
@Override
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
ActionEntry entry = request.actionEntry;
|
||||
if (entry.rpconly && !request.rpc) {
|
||||
response.finish(503, null);
|
||||
return;
|
||||
if (entry.rpconly) {
|
||||
if (!request.rpc) {
|
||||
response.finish(404, null);
|
||||
return;
|
||||
} else if (request.rpcAuthenticator != null) {
|
||||
if (!request.rpcAuthenticator.auth(request, response)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entry.cacheseconds > 0) {//有缓存设置
|
||||
CacheEntry ce = entry.modeOneCache ? entry.oneCache : entry.cache.get(request.getRequestURI());
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public class HttpSimpleClient {
|
||||
|
||||
public static final String USER_AGENT = "Redkale-http-client/" + Redkale.getDotedVersion();
|
||||
|
||||
protected final AsyncGroup asyncGroup;
|
||||
|
||||
protected int readTimeoutSeconds = 6;
|
||||
@@ -103,7 +105,7 @@ public class HttpSimpleClient {
|
||||
+ "Host: " + uri.getHost() + "\r\n"
|
||||
+ "Content-Length: " + (body == null ? 0 : body.length) + "\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
if (headers == null || !headers.containsKey("User-Agent")) {
|
||||
array.put(("User-Agent: Redkale-http-client/" + Redkale.getDotedVersion() + "\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
array.put(("User-Agent: " + USER_AGENT + "\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
if (headers == null || !headers.containsKey("Connection")) {
|
||||
array.put(("Connection: close\r\n").getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class Rest {
|
||||
|
||||
public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name";
|
||||
|
||||
public static final String REST_HEADER_RPC_NAME = "rest-rpc-name";
|
||||
public static final String REST_HEADER_RPC = "rest-rpc";
|
||||
|
||||
public static final String REST_HEADER_CURRUSERID_NAME = "rest-curruserid-name";
|
||||
|
||||
@@ -155,9 +155,13 @@ public final class Rest {
|
||||
}
|
||||
}
|
||||
|
||||
static JsonConvert createJsonConvert(RestConvert[] converts, RestConvertCoder[] coders) {
|
||||
if ((converts == null || converts.length < 1) && (coders == null || coders.length < 1)) return JsonConvert.root();
|
||||
final JsonFactory childFactory = JsonFactory.create();
|
||||
public static JsonFactory createJsonFactory(RestConvert[] converts, RestConvertCoder[] coders) {
|
||||
return createJsonFactory(true, converts, coders);
|
||||
}
|
||||
|
||||
public static JsonFactory createJsonFactory(boolean tiny, RestConvert[] converts, RestConvertCoder[] coders) {
|
||||
if ((converts == null || converts.length < 1) && (coders == null || coders.length < 1)) return JsonFactory.root();
|
||||
final JsonFactory childFactory = JsonFactory.create().tiny(tiny);
|
||||
List<Class> types = new ArrayList<>();
|
||||
Set<Class> reloadTypes = new HashSet<>();
|
||||
if (coders != null) {
|
||||
@@ -169,25 +173,28 @@ public final class Rest {
|
||||
if (converts != null) {
|
||||
for (RestConvert rc : converts) {
|
||||
if (rc.type() == void.class || rc.type() == Void.class) {
|
||||
return JsonFactory.create().skipAllIgnore(true).getConvert();
|
||||
return JsonFactory.create().skipAllIgnore(true);
|
||||
}
|
||||
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
|
||||
if (rc.skipIgnore()) {
|
||||
childFactory.registerSkipIgnore(rc.type());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
} else if (rc.onlyColumns().length > 0) {
|
||||
childFactory.registerIgnoreAll(rc.type(), rc.onlyColumns());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
} else {
|
||||
childFactory.register(rc.type(), false, rc.convertColumns());
|
||||
childFactory.register(rc.type(), true, rc.ignoreColumns());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
}
|
||||
types.add(rc.type());
|
||||
childFactory.tiny(rc.tiny());
|
||||
if (tiny) childFactory.tiny(rc.tiny());
|
||||
}
|
||||
}
|
||||
for (Class type : reloadTypes) {
|
||||
childFactory.reloadCoder(type);
|
||||
}
|
||||
return childFactory.getConvert();
|
||||
return childFactory;
|
||||
}
|
||||
|
||||
static String getWebModuleNameLowerCase(Class<? extends Service> serviceType) {
|
||||
@@ -916,6 +923,10 @@ public final class Rest {
|
||||
final String multiContextDesc = Type.getDescriptor(MultiContext.class);
|
||||
final String multiContextName = MultiContext.class.getName().replace('.', '/');
|
||||
final String mappingDesc = Type.getDescriptor(HttpMapping.class);
|
||||
final String restConvertDesc = Type.getDescriptor(RestConvert.class);
|
||||
final String restConvertsDesc = Type.getDescriptor(RestConvert.RestConverts.class);
|
||||
final String restConvertCoderDesc = Type.getDescriptor(RestConvertCoder.class);
|
||||
final String restConvertCodersDesc = Type.getDescriptor(RestConvertCoder.RestConvertCoders.class);
|
||||
final String httpParamDesc = Type.getDescriptor(HttpParam.class);
|
||||
final String httpParamsDesc = Type.getDescriptor(HttpParam.HttpParams.class);
|
||||
final String sourcetypeDesc = Type.getDescriptor(HttpParam.HttpParameterStyle.class);
|
||||
@@ -1277,7 +1288,7 @@ public final class Rest {
|
||||
genField.setAccessible(true);
|
||||
Object[] rc = restConverts.get(i);
|
||||
|
||||
genField.set(obj, createJsonConvert((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]));
|
||||
genField.set(obj, createJsonFactory((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]).getConvert());
|
||||
}
|
||||
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
|
||||
typesfield.setAccessible(true);
|
||||
@@ -1574,6 +1585,7 @@ public final class Rest {
|
||||
n = annhead.name();
|
||||
radix = annhead.radix();
|
||||
comment = annhead.comment();
|
||||
required = false;
|
||||
if (n.isEmpty()) throw new RuntimeException("@RestHeader.value is illegal in " + method);
|
||||
}
|
||||
RestCookie anncookie = param.getAnnotation(RestCookie.class);
|
||||
@@ -1583,6 +1595,7 @@ public final class Rest {
|
||||
n = anncookie.name();
|
||||
radix = anncookie.radix();
|
||||
comment = anncookie.comment();
|
||||
required = false;
|
||||
if (n.isEmpty()) throw new RuntimeException("@RestCookie.value is illegal in " + method);
|
||||
}
|
||||
RestSessionid annsid = param.getAnnotation(RestSessionid.class);
|
||||
@@ -1590,6 +1603,7 @@ public final class Rest {
|
||||
if (annhead != null) throw new RuntimeException("@RestSessionid and @RestHeader cannot on the same Parameter in " + method);
|
||||
if (anncookie != null) throw new RuntimeException("@RestSessionid and @RestCookie cannot on the same Parameter in " + method);
|
||||
if (ptype != String.class) throw new RuntimeException("@RestSessionid must on String Parameter in " + method);
|
||||
required = false;
|
||||
}
|
||||
RestAddress annaddr = param.getAnnotation(RestAddress.class);
|
||||
if (annaddr != null) {
|
||||
@@ -1598,6 +1612,7 @@ public final class Rest {
|
||||
if (annsid != null) throw new RuntimeException("@RestAddress and @RestSessionid cannot on the same Parameter in " + method);
|
||||
if (ptype != String.class) throw new RuntimeException("@RestAddress must on String Parameter in " + method);
|
||||
comment = annaddr.comment();
|
||||
required = false;
|
||||
}
|
||||
RestBody annbody = param.getAnnotation(RestBody.class);
|
||||
if (annbody != null) {
|
||||
@@ -1644,6 +1659,7 @@ public final class Rest {
|
||||
if (annfile != null) throw new RuntimeException("@RestUserid and @RestUploadFile cannot on the same Parameter in " + method);
|
||||
if (!ptype.isPrimitive() && !java.io.Serializable.class.isAssignableFrom(ptype)) throw new RuntimeException("@RestUserid must on java.io.Serializable Parameter in " + method);
|
||||
comment = "";
|
||||
required = false;
|
||||
}
|
||||
|
||||
RestHeaders annheaders = param.getAnnotation(RestHeaders.class);
|
||||
@@ -1657,6 +1673,7 @@ public final class Rest {
|
||||
if (userid != null) throw new RuntimeException("@RestHeaders and @RestUserid cannot on the same Parameter in " + method);
|
||||
if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map<String, String> Parameter in " + method);
|
||||
comment = "";
|
||||
required = false;
|
||||
}
|
||||
RestParams annparams = param.getAnnotation(RestParams.class);
|
||||
if (annparams != null) {
|
||||
@@ -1710,6 +1727,7 @@ public final class Rest {
|
||||
}
|
||||
|
||||
Map<String, Object> mappingMap = new LinkedHashMap<>();
|
||||
java.lang.reflect.Type returnGenericNoFutureType = TypeToken.getGenericType(method.getGenericReturnType(), serviceType);
|
||||
{ // 设置 Annotation
|
||||
//设置 HttpMapping
|
||||
boolean reqpath = false;
|
||||
@@ -1741,23 +1759,22 @@ public final class Rest {
|
||||
}
|
||||
av1.visitEnd();
|
||||
|
||||
java.lang.reflect.Type grt = TypeToken.getGenericType(method.getGenericReturnType(), serviceType);
|
||||
Class rtc = returnType;
|
||||
if (rtc == void.class) {
|
||||
rtc = RetResult.class;
|
||||
grt = TYPE_RETRESULT_STRING;
|
||||
returnGenericNoFutureType = TYPE_RETRESULT_STRING;
|
||||
} else if (CompletionStage.class.isAssignableFrom(returnType)) {
|
||||
ParameterizedType ptgrt = (ParameterizedType) grt;
|
||||
grt = ptgrt.getActualTypeArguments()[0];
|
||||
rtc = TypeToken.typeToClass(grt);
|
||||
ParameterizedType ptgrt = (ParameterizedType) returnGenericNoFutureType;
|
||||
returnGenericNoFutureType = ptgrt.getActualTypeArguments()[0];
|
||||
rtc = TypeToken.typeToClass(returnGenericNoFutureType);
|
||||
if (rtc == null) rtc = Object.class; //应该不会发生吧?
|
||||
}
|
||||
av0.visit("result", Type.getType(Type.getDescriptor(rtc)));
|
||||
if (grt != rtc) {
|
||||
String refid = typeRefs.get(grt);
|
||||
if (returnGenericNoFutureType != rtc) {
|
||||
String refid = typeRefs.get(returnGenericNoFutureType);
|
||||
if (refid == null) {
|
||||
refid = "_typeref_" + typeRefs.size();
|
||||
typeRefs.put(grt, refid);
|
||||
typeRefs.put(returnGenericNoFutureType, refid);
|
||||
}
|
||||
av0.visit("resultref", refid);
|
||||
}
|
||||
@@ -1770,10 +1787,52 @@ public final class Rest {
|
||||
mappingMap.put("actionid", entry.actionid);
|
||||
mappingMap.put("comment", entry.comment);
|
||||
mappingMap.put("methods", entry.methods);
|
||||
mappingMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
|
||||
mappingMap.put("result", returnGenericNoFutureType == returnType ? returnType.getName() : String.valueOf(returnGenericNoFutureType));
|
||||
entry.mappingurl = url;
|
||||
}
|
||||
|
||||
if (rcs != null && rcs.length > 0) { // 设置 Annotation
|
||||
av0 = mv.visitAnnotation(restConvertsDesc, true);
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
//设置 RestConvert
|
||||
for (RestConvert rc : rcs) {
|
||||
AnnotationVisitor av2 = av1.visitAnnotation(null, restConvertDesc);
|
||||
av2.visit("tiny", rc.tiny());
|
||||
av2.visit("skipIgnore", rc.skipIgnore());
|
||||
av2.visit("type", Type.getType(Type.getDescriptor(rc.type())));
|
||||
AnnotationVisitor av3 = av2.visitArray("onlyColumns");
|
||||
for (String s : rc.onlyColumns()) {
|
||||
av3.visit(null, s);
|
||||
}
|
||||
av3.visitEnd();
|
||||
av3 = av2.visitArray("ignoreColumns");
|
||||
for (String s : rc.ignoreColumns()) {
|
||||
av3.visit(null, s);
|
||||
}
|
||||
av3.visitEnd();
|
||||
av3 = av2.visitArray("convertColumns");
|
||||
for (String s : rc.convertColumns()) {
|
||||
av3.visit(null, s);
|
||||
}
|
||||
av3.visitEnd();
|
||||
av2.visitEnd();
|
||||
}
|
||||
av1.visitEnd();
|
||||
av0.visitEnd();
|
||||
}
|
||||
if (rcc != null && rcc.length > 0) { // 设置 Annotation
|
||||
av0 = mv.visitAnnotation(restConvertCodersDesc, true);
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
//设置 RestConvertCoder
|
||||
for (RestConvertCoder rc : rcc) {
|
||||
AnnotationVisitor av2 = av1.visitAnnotation(null, restConvertCoderDesc);
|
||||
av2.visit("type", Type.getType(Type.getDescriptor(rc.type())));
|
||||
av2.visit("field", rc.field());
|
||||
av2.visit("coder", Type.getType(Type.getDescriptor(rc.coder())));
|
||||
av2.visitEnd();
|
||||
}
|
||||
av1.visitEnd();
|
||||
av0.visitEnd();
|
||||
}
|
||||
{ // 设置 Annotation
|
||||
av0 = mv.visitAnnotation(httpParamsDesc, true);
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
@@ -2251,7 +2310,7 @@ public final class Rest {
|
||||
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
varInsns.add(new int[]{ALOAD, maxLocals});
|
||||
JsonFactory.root().loadDecoder(param.getParameterizedType());
|
||||
JsonFactory.root().loadDecoder(pgentype);
|
||||
|
||||
//构建 RestHeader、RestCookie、RestAddress 等赋值操作
|
||||
Class loop = ptype;
|
||||
@@ -2572,22 +2631,34 @@ public final class Rest {
|
||||
} else if (CompletionStage.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;");
|
||||
MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + typeDesc + stageDesc + ")V", false);
|
||||
if (returnGenericNoFutureType == HttpScope.class) {
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishScope", "(" + convertDesc + stageDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishScope", "(" + stageDesc + ")V", false);
|
||||
}
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;");
|
||||
MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + typeDesc + stageDesc + ")V", false);
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;");
|
||||
MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + typeDesc + stageDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;");
|
||||
MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + typeDesc + stageDesc + ")V", false);
|
||||
}
|
||||
}
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
@@ -2872,7 +2943,7 @@ public final class Rest {
|
||||
Field genField = newClazz.getDeclaredField(REST_CONVERT_FIELD_PREFIX + (i + 1));
|
||||
genField.setAccessible(true);
|
||||
Object[] rc = restConverts.get(i);
|
||||
genField.set(obj, createJsonConvert((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]));
|
||||
genField.set(obj, createJsonFactory((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]).getConvert());
|
||||
RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), genField);
|
||||
}
|
||||
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
|
||||
|
||||
@@ -25,14 +25,47 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Repeatable(RestConvert.RestConverts.class)
|
||||
public @interface RestConvert {
|
||||
|
||||
/**
|
||||
* 是否输出空字符串,0数值
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean tiny() default true;
|
||||
|
||||
/**
|
||||
* 是否忽略ConvertColumn.ignore=true的设置, 优先级最高
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean skipIgnore() default false;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* @return Class
|
||||
*/
|
||||
Class type();
|
||||
|
||||
/**
|
||||
* 仅显示的字段, 优先级其次,有值就会忽略ignoreColumns、convertColumns值
|
||||
*
|
||||
* @since 2.7.0
|
||||
* @return String[]
|
||||
*/
|
||||
String[] onlyColumns() default {};
|
||||
|
||||
/**
|
||||
* 屏蔽的字段
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
String[] ignoreColumns() default {};
|
||||
|
||||
/**
|
||||
* 允许输出的字段
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
String[] convertColumns() default {};
|
||||
|
||||
@Inherited
|
||||
|
||||
@@ -645,6 +645,18 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
return _sendConvert;
|
||||
}
|
||||
|
||||
protected void setTextConvert(Convert convert) {
|
||||
this._textConvert = convert;
|
||||
}
|
||||
|
||||
protected void setBinaryConvert(Convert convert) {
|
||||
this._binaryConvert = convert;
|
||||
}
|
||||
|
||||
protected void setSendConvert(Convert convert) {
|
||||
this._sendConvert = convert;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/**
|
||||
* 获取指定userid的WebSocket数组, 没有返回null <br>
|
||||
|
||||
@@ -296,7 +296,7 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
if (packet.type == FrameType.TEXT) {
|
||||
try {
|
||||
Convert convert = webSocket.getTextConvert();
|
||||
if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
if (restMessageConsumer != null && convert != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageRestType, packet.getPayload()));
|
||||
} else {
|
||||
webSocket.onMessage(packet.getPayload() == null ? null : new String(packet.getPayload(), StandardCharsets.UTF_8), packet.last);
|
||||
@@ -307,7 +307,7 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
} else if (packet.type == FrameType.BINARY) {
|
||||
try {
|
||||
Convert convert = webSocket.getBinaryConvert();
|
||||
if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
if (restMessageConsumer != null && convert != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageRestType, packet.getPayload()));
|
||||
} else {
|
||||
webSocket.onMessage(packet.getPayload(), packet.last);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user