Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
630f18792a | ||
|
|
d0cb04a224 | ||
|
|
0e8ac2f43c | ||
|
|
0c60700d82 | ||
|
|
dc285b6c2f | ||
|
|
74009b38c4 | ||
|
|
e820be1de9 | ||
|
|
7db3cbd03d | ||
|
|
27d2433993 | ||
|
|
64eda4cdf7 | ||
|
|
f05961cf07 | ||
|
|
be713c9ccf | ||
|
|
00dc3ee945 | ||
|
|
927007774b | ||
|
|
f4994f66c9 | ||
|
|
aef973a4d9 | ||
|
|
ba618ceba0 | ||
|
|
7f22eca8dc | ||
|
|
862018b63f | ||
|
|
f38143ff7b | ||
|
|
481cde05bf | ||
|
|
a1e6413704 | ||
|
|
8d1b9a18b4 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
/target/
|
||||
/.idea/
|
||||
/redkale.iml
|
||||
|
||||
16
README.md
16
README.md
@@ -1,27 +1,27 @@
|
||||
<h1>项目介绍</h1>
|
||||
<b>项目介绍</b>
|
||||
<p>
|
||||
Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 11全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。
|
||||
</p>
|
||||
<strong>RedKale 有如下主要特点:</strong>
|
||||
<ol>
|
||||
<li>大量使用Java 8新特性(接口默认值、Stream、Lambda、JDk8内置的ASM等)</li>
|
||||
<li>大量使用Java 8+新特性(接口默认值、Stream、Lambda、内置的ASM、HttpClient等)</li>
|
||||
<li>提供HTTP服务,同时内置JSON功能与限时缓存功能</li>
|
||||
<li>TCP层完全使用NIO,并统一TCP与UDP的接口换</li>
|
||||
<li>提供分布式与集中式部署的无缝切换</li>
|
||||
<li>提供类似JPA功能,包含数据缓存自动同步、分表分库与简洁的数据层操作接口</li>
|
||||
<li>可以动态修改已依赖注入的资源</li>
|
||||
</ol>
|
||||
</ol>
|
||||
|
||||
<strong>Redkale 设计理念</strong>
|
||||
<p>
|
||||
作为一个全新的微服务框架,Redkale在接口定义上使用了Java 8大量的新语法,接口有默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。<br/>
|
||||
与主流框架比,功能上Redkale显得很简单,这体现了Redkale的简易性,而并非是不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,如Redkale的HTTP服务不支持HTTPS和JSP,HTTPS比HTTP多了一层加密解密,这种密集型的计算不是Java的专长,通常提供HTTP服务的架构不会将Java动态服务器放在最前端,而是在前方会放nginx或apache,除了负载均衡还能静动分离,因此HTTPS的加解密应交给nginx这样的高性能服务器处理。Redkale再提供HTTPS服务就显得鸡肋。JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能方便兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选,不会为了迎合主流而提供,而是以良好的设计思想为指导。这是Redkale的主导思维。
|
||||
作为一个全新的微服务框架,Redkale在接口定义上使用了Java 8以上版本的大量新特性,接口有默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。<br/>
|
||||
与主流框架比,功能上Redkale显得很简单,这体现了Redkale的简易性,而并非是不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,如Redkale的HTTP服务不支持JSP, JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能方便兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选,不会为了迎合主流而提供,而是以良好的设计思想为指导。这是Redkale的主导思维。
|
||||
</p>
|
||||
|
||||
|
||||
<h5>详情请访问: <a href='https://redkale.org' target='_blank'>https://redkale.org</a></h5>
|
||||
<b>详情请访问: <a href='https://redkale.org' target='_blank'>https://redkale.org</a></b>
|
||||
|
||||
<h5>基本文档: <a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
|
||||
<b>基本文档: <a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></b>
|
||||
|
||||
<h5>欢迎加入Redkale QQ群: 527523235</h5>
|
||||
<b>欢迎加入Redkale QQ群: 527523235</b>
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DCMD=APIDOC -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
7
bin/apidoc.cmd
Normal file
7
bin/apidoc.cmd
Normal file
@@ -0,0 +1,7 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application apidoc
|
||||
@@ -6,7 +6,7 @@ APP_HOME=`dirname "$0"`
|
||||
|
||||
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
|
||||
APP_HOME="$APP_HOME"/..
|
||||
fi
|
||||
fi
|
||||
|
||||
lib='.'
|
||||
for jar in `ls $APP_HOME/lib/*.jar`
|
||||
@@ -15,4 +15,4 @@ do
|
||||
done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
echo "$APP_HOME"
|
||||
java -DCMD=APIDOC -DAPP_HOME="$APP_HOME" org.redkale.boot.Application
|
||||
java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application apidoc
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DCMD=%1 -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
7
bin/redkale.cmd
Normal file
7
bin/redkale.cmd
Normal file
@@ -0,0 +1,7 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application %*
|
||||
@@ -20,4 +20,4 @@ done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
|
||||
echo "$APP_HOME"
|
||||
java -DCMD=$1 -DAPP_HOME="$APP_HOME" org.redkale.boot.Application
|
||||
java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application $@ &
|
||||
@@ -4,6 +4,6 @@ SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
call "%APP_HOME%\bin\shutdown.bat"
|
||||
call "%APP_HOME%\bin\shutdown.cmd"
|
||||
|
||||
call "%APP_HOME%\bin\start.bat"
|
||||
call "%APP_HOME%\bin\start.cmd"
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
<application nodeid="10000" port="2020">
|
||||
|
||||
<resources>
|
||||
|
||||
<resources>
|
||||
<properties load="config.properties">
|
||||
<property name="system.property.redkale.convert.protobuf.enumtostring" value="true"/>
|
||||
</properties>
|
||||
</resources>
|
||||
|
||||
<server protocol="HTTP" port="5050">
|
||||
|
||||
<server protocol="HTTP" port="5050">
|
||||
<request>
|
||||
<remoteaddr value="request.headers.X-RemoteAddress"/>
|
||||
</request>
|
||||
|
||||
|
||||
<response>
|
||||
<defcookie domain="" path="/"/>
|
||||
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
|
||||
@@ -24,8 +25,7 @@
|
||||
|
||||
<rest path="/pipes" />
|
||||
|
||||
<servlets path="/pipes" autoload="true" />
|
||||
|
||||
<servlets path="/pipes" autoload="true" />
|
||||
</server>
|
||||
|
||||
</application>
|
||||
|
||||
2
conf/config.properties
Normal file
2
conf/config.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
#
|
||||
@@ -18,8 +18,8 @@ java.util.logging.FileHandler.level = FINER
|
||||
java.util.logging.FileHandler.limit = 10M
|
||||
java.util.logging.FileHandler.count = 20
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%tY%tm/log-%tY%tm%td.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%tY%tm/log-warnerr-%tY%tm%td.log
|
||||
java.util.logging.FileHandler.append = true
|
||||
|
||||
java.util.logging.ConsoleHandler.level = FINEST
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.0">
|
||||
|
||||
<persistence-unit name="" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?autoReconnect=true&characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<!--
|
||||
<persistence-unit name="user.read" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
|
||||
<property name="javax.persistence.jdbc.user" value="system"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<persistence-unit name="user.write" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
-->
|
||||
|
||||
</persistence>
|
||||
13
conf/source.properties
Normal file
13
conf/source.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
############ DataSource @Resource(name="platf") ############
|
||||
#redkale.datasource[platf].url = jdbc:mysql://127.0.0.1:3306/platf?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
#redkale.datasource[platf].user = root
|
||||
#redkale.datasource[platf].password = 12345678
|
||||
### true: auto ddl;
|
||||
#redkale.datasource[platf].table-autoddl = true
|
||||
|
||||
|
||||
############ CacheSource @Resource(name="usersession") ############
|
||||
#redkale.cachesource[usersession].node[0].url = redis://127.0.0.1:6363
|
||||
#redkale.cachesource[usersession].node[0].password = 12345678
|
||||
#redkale.cachesource[usersession].node[0].db = 0
|
||||
18
my/gitrun.sh
Normal file
18
my/gitrun.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
export LC_ALL="zh_CN.UTF-8"
|
||||
|
||||
rm -fr redkale
|
||||
|
||||
rm -fr src
|
||||
rm -fr bin
|
||||
rm -fr conf
|
||||
|
||||
git clone https://github.com/redkale/redkale.git
|
||||
|
||||
cp -fr redkale/src ./
|
||||
cp -fr redkale/bin ./
|
||||
cp -fr redkale/conf ./
|
||||
|
||||
mvn clean
|
||||
mvn deploy
|
||||
23
my/pom.xml
23
my/pom.xml
@@ -7,19 +7,26 @@
|
||||
<name>RedkaleProject</name>
|
||||
<url>http://redkale.org</url>
|
||||
<description>redkale -- java framework</description>
|
||||
<version>2.5.0</version>
|
||||
<version>2.7.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
|
||||
<junit.version>5.7.0</junit.version>
|
||||
<maven-plugin.version>3.2.0</maven-plugin.version>
|
||||
<maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
|
||||
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||
<maven-failsafe-plugin.version>3.0.0-M5</maven-failsafe-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.7.0</version>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -74,7 +81,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<compilerArgument>-parameters</compilerArgument>
|
||||
<encoding>UTF-8</encoding>
|
||||
@@ -87,7 +94,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
@@ -101,7 +108,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>${maven-gpg-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
@@ -116,7 +123,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -129,7 +136,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -142,7 +149,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptors>
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
<EFBFBD><EFBFBD>Ŀ¼<EFBFBD>µ<EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatypeʱʹ<EFBFBD>ã<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><DAB9>̴<EFBFBD><CCB4><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD>Ŀ¼<EFBFBD>µ<EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatypeʱʹ<EFBFBD>ã<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><DAB9>̴<EFBFBD><CCB4><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
ʹ<EFBFBD><EFBFBD>gpg<EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatype<EFBFBD><EFBFBD>Կ:
|
||||
|
||||
1<EFBFBD><EFBFBD> gpg <20>C-gen-key
|
||||
2<EFBFBD><EFBFBD> gpg --keyserver keys.openpgp.org --send-keys <20><><EFBFBD>Ĺ<EFBFBD>Կ(һ<><D2BB>ʮ<EFBFBD><CAAE><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>֣<EFBFBD><D6A3><EFBFBD><EFBFBD><EFBFBD>DE346FA5)
|
||||
<20><>ʾ<EFBFBD><CABE> gpg: <20>ӹ<EFBFBD>Կ<EFBFBD><D4BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>Server indicated a failure <20><>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<id>release</id>
|
||||
<!--
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
16
pom.xml
16
pom.xml
@@ -7,7 +7,7 @@
|
||||
<name>RedkaleProject</name>
|
||||
<url>https://redkale.org</url>
|
||||
<description>redkale -- java framework</description>
|
||||
<version>2.6.0</version>
|
||||
<version>2.7.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@@ -15,10 +15,10 @@
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
|
||||
<junit.version>5.7.0</junit.version>
|
||||
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||
<maven-failsafe-plugin.version>3.0.0-M5</maven-failsafe-plugin.version>
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||
<maven-compiler-plugin.version>3.9.0</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M6</maven-surefire-plugin.version>
|
||||
<maven-failsafe-plugin.version>3.0.0-M6</maven-failsafe-plugin.version>
|
||||
</properties>
|
||||
|
||||
<licenses>
|
||||
@@ -95,7 +95,7 @@
|
||||
<plugin>
|
||||
<groupId>org.redkale.maven.plugins</groupId>
|
||||
<artifactId>redkale-maven-plugin</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>1.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>redkale-compile</id>
|
||||
@@ -125,6 +125,10 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<forkMode>once</forkMode>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
|
||||
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
|
||||
@@ -20,7 +20,7 @@
|
||||
-->
|
||||
<!--
|
||||
nodeid: int 进程的节点ID,用于分布式环境,一个系统中节点ID必须全局唯一,使用cluster时框架会进行唯一性校验
|
||||
name: 进程的名称,用于监控识别,命名规则: 字母、数字、下划线
|
||||
name: 进程的名称,用于监控识别,命名规则: 字母、数字、下划线、短横、点
|
||||
address: 本地局域网的IP地址, 默认值为默认网卡的ip,当不使用默认值需要指定值,如192.168.1.22
|
||||
port: required 程序的管理Server的端口,用于关闭或者与监管系统进行数据交互
|
||||
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
|
||||
@@ -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"/>
|
||||
@@ -282,10 +273,13 @@
|
||||
当Server为HTTP协议时, request节点才有效。
|
||||
remoteaddr 节点: 替换请求方节点的IP地址, 通常请求方是由nginx等web静态服务器转发过的则需要配置该节点。
|
||||
且value值只能是以request.headers.开头,表示从request.headers中获取对应的header值。
|
||||
locale value值必须是request.headers.或request.parameters.开头。
|
||||
例如下面例子获取request.getRemoteAddr()值,如果header存在X-RemoteAddress值则返回X-RemoteAddress值,不存在返回getRemoteAddress()。
|
||||
-->
|
||||
<request>
|
||||
<remoteaddr value="request.headers.X-RemoteAddress"/>
|
||||
<locale value="request.headers.locale" />
|
||||
<rpc authenticator="org.redkale.net.http.HttpRpcAuthenticator的实现类"/>
|
||||
</request>
|
||||
|
||||
<!--
|
||||
@@ -294,8 +288,8 @@
|
||||
contenttype: plain值为调用finish时的ContentType; 默认值: text/plain; charset=utf-8
|
||||
json值为调用finishJson时的ContentType; 默认值: application/json; charset=utf-8
|
||||
defcookie 节点: 当response里输出的cookie没有指定domain 和path时,使用该节点的默认值。
|
||||
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
|
||||
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
|
||||
addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
|
||||
addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
|
||||
例如下面例子是在Response输出header时添加两个header(一个addHeader, 一个setHeader)。
|
||||
options 节点: 设置了该节点且auto=true,当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
|
||||
date 节点: 设置了该节点且period有值(单位:毫秒);返回response会包含Date头信息,默认为period=0
|
||||
|
||||
@@ -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,14 +84,14 @@ 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;
|
||||
if (plainContentType == null || plainContentType.isEmpty()) plainContentType = "application/json";
|
||||
if (plainContentType.indexOf(';') > 0) plainContentType = plainContentType.substring(0, plainContentType.indexOf(';'));
|
||||
|
||||
for (HttpServlet servlet : server.getPrepareServlet().getServlets()) {
|
||||
for (HttpServlet servlet : server.getDispatcherServlet().getServlets()) {
|
||||
if (!(servlet instanceof HttpServlet)) continue;
|
||||
if (servlet instanceof WebSocketServlet) continue;
|
||||
if (servlet.getClass().getAnnotation(MessageMultiConsumer.class) != null) {
|
||||
@@ -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,47 @@ public class LoggingFileHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoggingConsoleHandler extends ConsoleHandler {
|
||||
|
||||
private Pattern denyreg;
|
||||
|
||||
public LoggingConsoleHandler() {
|
||||
super();
|
||||
setFormatter(new LoggingFormater());
|
||||
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 +94,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 +150,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 +168,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 +248,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 +289,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 +298,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 +311,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 +389,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 +403,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());
|
||||
@@ -165,7 +165,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
|
||||
RedkaleClassLoader.putReflectionPublicClasses(HttpServlet.class.getName());
|
||||
RedkaleClassLoader.putReflectionPublicClasses(HttpPrepareServlet.class.getName());
|
||||
RedkaleClassLoader.putReflectionPublicClasses(HttpDispatcherServlet.class.getName());
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(HttpResourceServlet.class, HttpResourceServlet.class.getName());
|
||||
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
|
||||
@@ -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,24 +429,27 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
long et = System.currentTimeMillis();
|
||||
application.servicecdl.countDown();
|
||||
application.servicecdl.await();
|
||||
logger.info(this.getClass().getSimpleName() + " construct services in " + (et - starts) + " ms and await " + (System.currentTimeMillis() - et) + " ms");
|
||||
|
||||
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 +707,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 +717,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 +743,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 +755,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 + "}";
|
||||
|
||||
@@ -42,6 +42,13 @@ public abstract class Convert<R extends Reader, W extends Writer> {
|
||||
return writer;
|
||||
}
|
||||
|
||||
protected <S extends W> S fieldFunc(S writer, BiFunction<Object, Object, Object> mapFieldFunc, BiFunction<Attribute, Object, Object> objFieldFunc, Function<Object, ConvertField[]> objExtFunc) {
|
||||
writer.mapFieldFunc = mapFieldFunc;
|
||||
writer.objFieldFunc = objFieldFunc;
|
||||
writer.objExtFunc = objExtFunc;
|
||||
return writer;
|
||||
}
|
||||
|
||||
public abstract Convert<R, W> newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc);
|
||||
|
||||
public abstract Convert<R, W> newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc, Function<Object, ConvertField[]> objExtFunc);
|
||||
|
||||
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.*;
|
||||
@@ -61,6 +61,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|
||||
private final Set<Class> skipIgnores = new HashSet();
|
||||
|
||||
final Set<String> ignoreMapColumns = new HashSet();
|
||||
|
||||
//key:需要屏蔽的字段;value:排除的字段名
|
||||
private final ConcurrentHashMap<Class, Set<String>> ignoreAlls = new ConcurrentHashMap();
|
||||
|
||||
@@ -248,7 +250,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 +275,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 +410,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 +493,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);
|
||||
@@ -529,7 +709,9 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
if (set == null) {
|
||||
ignoreAlls.put(type, new HashSet<>(Arrays.asList(excludeColumns)));
|
||||
} else {
|
||||
set.addAll(Arrays.asList(excludeColumns));
|
||||
synchronized (set) {
|
||||
set.addAll(Arrays.asList(excludeColumns));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,17 +720,47 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
if (set == null) {
|
||||
ignoreAlls.put(type, new HashSet<>(excludeColumns));
|
||||
} else {
|
||||
set.addAll(new ArrayList(excludeColumns));
|
||||
synchronized (set) {
|
||||
set.addAll(new ArrayList(excludeColumns));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void register(final Class type, boolean ignore, String... columns) {
|
||||
if (type == Map.class) {
|
||||
synchronized (ignoreMapColumns) {
|
||||
if (ignore) {
|
||||
for (String column : columns) {
|
||||
ignoreMapColumns.add(column);
|
||||
}
|
||||
} else {
|
||||
for (String column : columns) {
|
||||
ignoreMapColumns.remove(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (String column : columns) {
|
||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
||||
}
|
||||
}
|
||||
|
||||
public final void register(final Class type, boolean ignore, Collection<String> columns) {
|
||||
if (type == Map.class) {
|
||||
synchronized (ignoreMapColumns) {
|
||||
if (ignore) {
|
||||
for (String column : columns) {
|
||||
ignoreMapColumns.add(column);
|
||||
}
|
||||
} else {
|
||||
for (String column : columns) {
|
||||
ignoreMapColumns.remove(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (String column : columns) {
|
||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
||||
}
|
||||
@@ -600,8 +812,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 +929,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 +946,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 +972,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 +1045,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 +1062,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 +1085,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 +1126,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) {
|
||||
|
||||
@@ -7,7 +7,8 @@ package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Map的序列化操作类
|
||||
@@ -32,6 +33,8 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
protected final Set<String> ignoreMapColumns;
|
||||
|
||||
public MapEncoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
try {
|
||||
@@ -43,6 +46,9 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
this.keyEncoder = factory.getAnyEncoder();
|
||||
this.valueEncoder = factory.getAnyEncoder();
|
||||
}
|
||||
synchronized (factory.ignoreMapColumns) {
|
||||
this.ignoreMapColumns = factory.ignoreMapColumns.isEmpty() ? null : new HashSet<>(factory.ignoreMapColumns);
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
@@ -74,11 +80,15 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<String> ignoreColumns = this.ignoreMapColumns;
|
||||
BiFunction<K, V, V> mapFieldFunc = (BiFunction) out.mapFieldFunc;
|
||||
if (out.writeMapB(values.size(), (Encodeable) keyEncoder, (Encodeable) valueEncoder, value) < 0) {
|
||||
boolean first = true;
|
||||
for (Map.Entry<K, V> en : values.entrySet()) {
|
||||
if (ignoreColumns != null && ignoreColumns.contains(en.getKey())) continue;
|
||||
V v = mapFieldFunc == null ? en.getValue() : mapFieldFunc.apply(en.getKey(), en.getValue());
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, en.getKey(), en.getValue(), first);
|
||||
writeMemberValue(out, member, en.getKey(), v, first);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
@@ -96,6 +106,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);
|
||||
|
||||
@@ -25,6 +25,9 @@ public abstract class Writer {
|
||||
//convertTo时是否以指定Type的ObjectEncoder进行处理
|
||||
protected Type specify;
|
||||
|
||||
//对某个key值进行动态处理,仅供MapEncoder使用
|
||||
protected BiFunction<Object, Object, Object> mapFieldFunc;
|
||||
|
||||
//对某个字段值进行动态处理
|
||||
protected BiFunction<Attribute, Object, Object> objFieldFunc;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -55,16 +58,25 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) {
|
||||
return newConvert(fieldFunc, null);
|
||||
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc) {
|
||||
return newConvert(objFieldFunc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc, Function<Object, ConvertField[]> objExtFunc) {
|
||||
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc, Function<Object, ConvertField[]> objExtFunc) {
|
||||
return new JsonConvert(getFactory(), tiny) {
|
||||
@Override
|
||||
protected <S extends JsonWriter> S configWrite(S writer) {
|
||||
return fieldFunc(writer, fieldFunc, objExtFunc);
|
||||
return fieldFunc(writer, objFieldFunc, objExtFunc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public JsonConvert newConvert(BiFunction<Object, Object, Object> mapFieldFunc, final BiFunction<Attribute, Object, Object> objFieldFunc, Function<Object, ConvertField[]> objExtFunc) {
|
||||
return new JsonConvert(getFactory(), tiny) {
|
||||
@Override
|
||||
protected <S extends JsonWriter> S configWrite(S writer) {
|
||||
return fieldFunc(writer, mapFieldFunc, objFieldFunc, objExtFunc);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -100,6 +112,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 +224,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实例
|
||||
@@ -68,16 +69,16 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
return httpServer().getContext();
|
||||
}
|
||||
|
||||
protected HttpPrepareServlet prepareServlet() {
|
||||
return (HttpPrepareServlet) httpServer().getPrepareServlet();
|
||||
protected HttpDispatcherServlet dispatcherServlet() {
|
||||
return (HttpDispatcherServlet) httpServer().getDispatcherServlet();
|
||||
}
|
||||
|
||||
protected HttpServlet findHttpServlet(String topic) {
|
||||
return prepareServlet().findServletByTopic(topic);
|
||||
return dispatcherServlet().findServletByTopic(topic);
|
||||
}
|
||||
|
||||
protected HttpServlet findHttpServlet(HttpSimpleRequest request) {
|
||||
return prepareServlet().findServletByTopic(generateHttpReqTopic(request, request.getPath()));
|
||||
return dispatcherServlet().findServletByTopic(generateHttpReqTopic(request, request.getPath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -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) {
|
||||
@@ -138,14 +141,15 @@ public class HttpMessageLocalClient extends HttpMessageClient {
|
||||
|
||||
@Override
|
||||
public void produceMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
|
||||
HttpPrepareServlet ps = prepareServlet();
|
||||
HttpDispatcherServlet ps = dispatcherServlet();
|
||||
HttpServlet servlet = ps.findServletByTopic(topic);
|
||||
if (servlet == null) {
|
||||
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) {
|
||||
@@ -155,9 +159,10 @@ 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);
|
||||
HttpDispatcherServlet ps = dispatcherServlet();
|
||||
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,12 +30,14 @@ 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;
|
||||
}
|
||||
|
||||
public void setRequestURI(String uri) {
|
||||
@Override
|
||||
public HttpMessageRequest setRequestURI(String uri) {
|
||||
this.requestURI = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -42,7 +42,7 @@ public class Context {
|
||||
protected final int bufferCapacity;
|
||||
|
||||
//服务的根Servlet
|
||||
protected final PrepareServlet prepare;
|
||||
protected final DispatcherServlet prepare;
|
||||
|
||||
//日志Logger
|
||||
protected final Logger logger;
|
||||
@@ -85,7 +85,7 @@ public class Context {
|
||||
|
||||
public Context(long serverStartTime, Logger logger, ExecutorService workExecutor, SSLBuilder sslBuilder, SSLContext sslContext,
|
||||
int bufferCapacity, final int maxconns, final int maxbody, Charset charset, InetSocketAddress address,
|
||||
ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
ResourceFactory resourceFactory, DispatcherServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
this.serverStartTime = serverStartTime;
|
||||
this.logger = logger;
|
||||
this.workExecutor = workExecutor;
|
||||
@@ -110,7 +110,7 @@ public class Context {
|
||||
}
|
||||
}
|
||||
|
||||
protected void executePrepareServlet(Request request, Response response) {
|
||||
protected void executeDispatcher(Request request, Response response) {
|
||||
if (workHashExecutor != null) {
|
||||
workHashExecutor.execute(request.getHashid(), () -> prepare.prepare(request, response));
|
||||
} else if (workExecutor != null) {
|
||||
@@ -124,7 +124,7 @@ public class Context {
|
||||
if (workHashExecutor != null) {
|
||||
workHashExecutor.execute(request.getHashid(), () -> {
|
||||
try {
|
||||
long cha = System.currentTimeMillis() - request.getCreatetime();
|
||||
long cha = System.currentTimeMillis() - request.getCreateTime();
|
||||
servlet.execute(request, response);
|
||||
if (cha > 1000 && response.context.logger.isLoggable(Level.WARNING)) {
|
||||
response.context.logger.log(Level.WARNING, "hash execute servlet delays=" + cha + "ms, request=" + request);
|
||||
@@ -234,7 +234,7 @@ public class Context {
|
||||
public int bufferCapacity;
|
||||
|
||||
//服务的根Servlet
|
||||
public PrepareServlet prepare;
|
||||
public DispatcherServlet prepare;
|
||||
|
||||
//服务的监听地址
|
||||
public InetSocketAddress address;
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 根Servlet, 一个Server只能存在一个根Servlet
|
||||
*
|
||||
* 由之前PrepareServlet更名而来,since 2.7.0
|
||||
* 用于分发Request请求
|
||||
*
|
||||
* <p>
|
||||
@@ -29,7 +29,7 @@ import org.redkale.util.*;
|
||||
* @param <P> Response的子类型
|
||||
* @param <S> Servlet的子类型
|
||||
*/
|
||||
public abstract class PrepareServlet<K extends Serializable, C extends Context, R extends Request<C>, P extends Response<C, R>, S extends Servlet<C, R, P>> extends Servlet<C, R, P> {
|
||||
public abstract class DispatcherServlet<K extends Serializable, C extends Context, R extends Request<C>, P extends Response<C, R>, S extends Servlet<C, R, P>> extends Servlet<C, R, P> {
|
||||
|
||||
protected final LongAdder executeCounter = new LongAdder(); //执行请求次数
|
||||
|
||||
@@ -62,6 +62,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
Set<S> newservlets = new HashSet<>(servlets);
|
||||
newservlets.remove(servlet);
|
||||
this.servlets = newservlets;
|
||||
doAfterRemove(servlet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +96,9 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
synchronized (lock2) {
|
||||
if (mappings.containsKey(key)) {
|
||||
Map<K, S> newmappings = new HashMap<>(mappings);
|
||||
newmappings.remove(key);
|
||||
S s = newmappings.remove(key);
|
||||
this.mappings = newmappings;
|
||||
doAfterRemove(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,9 +114,13 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
}
|
||||
for (K key : keys) newmappings.remove(key);
|
||||
this.mappings = newmappings;
|
||||
doAfterRemove(servlet);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doAfterRemove(S servlet) {
|
||||
}
|
||||
|
||||
protected S mappingServlet(K key) {
|
||||
return mappings.get(key);
|
||||
}
|
||||
@@ -215,6 +221,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
|
||||
*/
|
||||
@@ -133,7 +134,7 @@ class ProtocolCodec implements CompletionHandler<Integer, ByteBuffer> {
|
||||
final Request request = response.request;
|
||||
final int rs = request.readHeader(buffer, lastreq);
|
||||
if (rs < 0) { //表示数据格式不正确
|
||||
final PrepareServlet preparer = context.prepare;
|
||||
final DispatcherServlet preparer = context.prepare;
|
||||
LongAdder ec = preparer.executeCounter;
|
||||
if (ec != null) ec.increment();
|
||||
channel.offerBuffer(buffer);
|
||||
@@ -143,7 +144,7 @@ class ProtocolCodec implements CompletionHandler<Integer, ByteBuffer> {
|
||||
context.logger.log(Level.FINEST, "request.readHeader erroneous (" + rs + "), force to close channel ");
|
||||
}
|
||||
} else if (rs == 0) {
|
||||
final PrepareServlet preparer = context.prepare;
|
||||
final DispatcherServlet preparer = context.prepare;
|
||||
LongAdder ec = preparer.executeCounter;
|
||||
if (ec != null) ec.increment();
|
||||
int pindex = pipelineIndex;
|
||||
@@ -158,7 +159,7 @@ class ProtocolCodec implements CompletionHandler<Integer, ByteBuffer> {
|
||||
request.pipeline(pindex, pindex);
|
||||
channel.setReadBuffer((ByteBuffer) buffer.clear());
|
||||
}
|
||||
context.executePrepareServlet(request, response);
|
||||
context.executeDispatcher(request, response);
|
||||
if (pipeline) {
|
||||
final Response pipelineResponse = createResponse();
|
||||
try {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user