23 Commits
2.6.0 ... 2.7.0

Author SHA1 Message Date
Redkale
630f18792a 2022-07-07 09:22:31 +08:00
Redkale
d0cb04a224 Redkale 2.7.0 结束 2022-07-06 23:23:09 +08:00
Redkale
0e8ac2f43c 2022-07-06 23:21:14 +08:00
Redkale
0c60700d82 增加ResourceEvent 2022-07-06 22:44:44 +08:00
Redkale
dc285b6c2f 2022-07-06 22:03:29 +08:00
Redkale
74009b38c4 2022-07-06 16:42:39 +08:00
Redkale
e820be1de9 2022-07-06 16:29:56 +08:00
Redkale
7db3cbd03d 增加Environment 2022-07-06 16:09:30 +08:00
Redkale
27d2433993 增加ResourceListener.different功能 2022-07-05 14:34:59 +08:00
Redkale
64eda4cdf7 2022-07-04 09:18:24 +08:00
Redkale
f05961cf07 @WebServlet合并url,例如: /shop/*,/shop/info 合并成一个 /shop/* 2022-07-03 23:43:22 +08:00
Redkale
be713c9ccf 2022-07-02 22:45:11 +08:00
Redkale
00dc3ee945 2022-07-02 22:10:32 +08:00
Redkale
927007774b PrepareServlet 更名为 DispatcherServlet 2022-07-02 21:33:52 +08:00
Redkale
f4994f66c9 2022-07-02 21:02:21 +08:00
Redkale
aef973a4d9 增加RestLocale功能 2022-07-02 20:47:50 +08:00
Redkale
ba618ceba0 2022-06-28 12:08:03 +08:00
Redkale
7f22eca8dc 2022-06-27 20:04:14 +08:00
Redkale
862018b63f 修改getCreatetime 2022-06-27 19:56:07 +08:00
Redkale
f38143ff7b 2022-06-20 11:18:51 +08:00
Redkale
481cde05bf 2022-06-20 11:08:23 +08:00
Redkale
a1e6413704 Update README.md 2022-05-27 10:40:16 +08:00
Redkale
8d1b9a18b4 2.7.0-SNAPSHOT 2022-05-27 10:39:58 +08:00
201 changed files with 9813 additions and 3388 deletions

2
.gitignore vendored
View File

@@ -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

View File

@@ -1,27 +1,27 @@
<h1>项目介绍</h1>
<b>项目介绍</b>
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为一个全新的微服务框架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/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;与主流框架比功能上Redkale显得很简单这体现了Redkale的简易性而并非是不足从一个良好的设计习惯或架构上来看有些常用功能是不需要提供的如Redkale的HTTP服务不支持HTTPS和JSPHTTPS比HTTP多了一层加密解密这种密集型的计算不是Java的专长通常提供HTTP服务的架构不会将Java动态服务器放在最前端而是在前方会放nginx或apache除了负载均衡还能静动分离因此HTTPS的加解密应交给nginx这样的高性能服务器处理。Redkale再提供HTTPS服务就显得鸡肋。JSP其实算是一个落后的技术现在是一个多样化终端的时代终端不只局限于桌面程序和PC浏览器还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端这些都不是JSP能方便兼顾的而HTTP+JSON作为通用性接口可以避免重复开发模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选不会为了迎合主流而提供而是以良好的设计思想为指导。这是Redkale的主导思维。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为一个全新的微服务框架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/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;与主流框架比功能上Redkale显得很简单这体现了Redkale的简易性而并非是不足从一个良好的设计习惯或架构上来看有些常用功能是不需要提供的如Redkale的HTTP服务不支持JSP, JSP其实算是一个落后的技术现在是一个多样化终端的时代终端不只局限于桌面程序和PC浏览器还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端这些都不是JSP能方便兼顾的而HTTP+JSON作为通用性接口可以避免重复开发模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选不会为了迎合主流而提供而是以良好的设计思想为指导。这是Redkale的主导思维。
</p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>详情请访问:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org' target='_blank'>https://redkale.org</a></h5>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>详情请访问:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org' target='_blank'>https://redkale.org</a></b>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>基本文档:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>基本文档:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></b>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>欢迎加入Redkale QQ群: 527523235</h5>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>欢迎加入Redkale QQ群: 527523235</b>
&nbsp;

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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
View 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 %*

View File

@@ -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 $@ &

View File

@@ -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"

View File

@@ -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
View File

@@ -0,0 +1,2 @@
#

View File

@@ -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

View File

@@ -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&amp;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
View File

@@ -0,0 +1,13 @@
############ DataSource @Resource(name="platf") ############
#redkale.datasource[platf].url = jdbc:mysql://127.0.0.1:3306/platf?allowPublicKeyRetrieval=true&amp;rewriteBatchedStatements=true&amp;serverTimezone=UTC&amp;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
View 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

View File

@@ -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>

View File

@@ -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>

View File

@@ -20,7 +20,7 @@
</profile>
<profile>
<id>release</id>
<id>release</id>
<!--
<build>
<plugins>

16
pom.xml
View File

@@ -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>

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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&amp;autoReconnectForPools=true&amp;characterEncoding=utf8 -->
<!-- jdbc:mysql://127.0.0.1:3306/dbim?allowPublicKeyRetrieval=true&amp;rewriteBatchedStatements=true&amp;serverTimezone=UTC&amp;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"/>

View 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

View File

@@ -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 "";
}

View File

@@ -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 &#60;= 16777215 then sqltype is MEDIUMTEXT <br>
* if type==String and length &#62; 16777215 then sqltype is LONGTEXT <br>
* if type==byte[] and length &#60;= 65535 then sqltype is BLOB <br>
* if type==byte[] and length &#60;= 16777215 then sqltype is MEDIUMBLOB <br>
* if type==byte[] and length &#62; 16777215 then sqltype is LONGBLOB <br>
*
* @return int
*/

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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&lt;FilterEntry&lt;T&gt;&gt;
*/
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&lt;FilterEntry&lt;T&gt;&gt;
*/
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&lt;FilterEntry&lt;T&gt;&gt;
*/
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;
}
}
/**

View 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; //不能直接暴露外界访问
}
}

View File

@@ -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);
}

View 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.");
}
}
}
}

View File

@@ -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;

View File

@@ -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() {

View 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);
}

View File

@@ -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);

View File

@@ -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

View File

@@ -9,7 +9,7 @@ import java.lang.reflect.Type;
/**
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入WriterJSON则不写入。
*
*
* 详情见: 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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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 + "}";

View File

@@ -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);

View 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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 {};
}

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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 fieldsubclass不传父类会导致java.lang.NoSuchFieldError的bug
subclass = field.getDeclaringClass();
}
return Attribute.create(subclass, clazz, fieldalias, null, field, getter, setter, null);
}
}

View File

@@ -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值

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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];
}

View File

@@ -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());

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;
}
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
/**
* 读取字符串, 必须是"或者'包围的字符串值
*

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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&#60;String, String&#62;) <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;
}
}

View 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;
}
}

View File

@@ -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') {

View File

@@ -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值

View File

@@ -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

View File

@@ -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());

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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("\"");
}

View File

@@ -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);
}
}

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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()));
}
}

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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