70 Commits
2.2.0 ... 2.6.0

Author SHA1 Message Date
Redkale
6e21fe56e9 Redkale 2.6.0 结束 2021-12-01 09:52:52 +08:00
Redkale
f0ac042b3c Update README.md 2021-10-18 20:58:14 +08:00
Redkale
13a4264488 Redkale 2.5.0 结束 2021-10-18 20:48:42 +08:00
redkale
7ffb65cc38 Redkale 2.4.0 结束 2021-10-18 13:19:30 +08:00
Redkale
2464c360c0 Redkale 2.4.0 结束 2021-06-06 19:06:05 +08:00
Redkale
1917ccf35c Redkale 2.3.0 结束 2021-04-09 00:28:57 +08:00
Redkale
4f9c14da97 2021-01-20 19:25:29 +08:00
Redkale
58e23c1b8c 2021-01-20 19:13:38 +08:00
Redkale
34156fc092 2021-01-20 18:52:11 +08:00
Redkale
86fde56129 Convert增加convertMapToBytes方法 2021-01-20 18:15:25 +08:00
Redkale
d997d3a7bb 增加ConvertSmallString功能 2021-01-20 17:37:29 +08:00
Redkale
4e689855f4 2021-01-20 13:20:03 +08:00
Redkale
eace16d7fd 2021-01-20 13:11:20 +08:00
Redkale
53b7f9b26f 2021-01-20 11:45:56 +08:00
Redkale
983c667725 2021-01-20 11:35:51 +08:00
Redkale
96fd660d46 2021-01-20 10:14:52 +08:00
Redkale
7d1770da8a 2021-01-19 17:40:08 +08:00
Redkale
b83f6867f3 2021-01-19 17:00:28 +08:00
Redkale
fa7db42f50 ObjectPool关闭public构造函数 2021-01-19 16:45:00 +08:00
Redkale
80c07a5c5b 2021-01-19 15:44:31 +08:00
Redkale
59a4c85aeb 2021-01-19 15:39:53 +08:00
Redkale
7fc01d38e8 2021-01-18 17:29:26 +08:00
Redkale
3010422f4e 2021-01-16 19:27:52 +08:00
Redkale
1f78bb1a98 2021-01-16 19:25:18 +08:00
Redkale
1d7f32efe4 2021-01-16 18:26:08 +08:00
Redkale
1e3d980437 2021-01-15 15:07:30 +08:00
Redkale
8654b5eef8 2021-01-14 13:37:45 +08:00
Redkale
5781ad1de0 2021-01-14 12:25:23 +08:00
Redkale
e2a1fa15d3 增加CacheClusterAgent功能 2021-01-14 12:01:25 +08:00
Redkale
50da7413d8 2021-01-13 09:24:35 +08:00
Redkale
e45b945024 2021-01-13 09:22:55 +08:00
Redkale
f3668a77ef 2021-01-12 21:39:56 +08:00
Redkale
2aee6cab20 2021-01-12 20:54:23 +08:00
Redkale
6233a0b960 2021-01-12 20:34:39 +08:00
Redkale
0ae469d8e2 2021-01-12 18:38:10 +08:00
Redkale
bcad47ebd5 修复HttpSimpleRequest的path编码bug 2021-01-12 17:59:33 +08:00
Redkale
71d179b8c3 2021-01-12 17:53:13 +08:00
Redkale
af35e5100e 2021-01-12 10:23:28 +08:00
Redkale
96c97c7dd2 2021-01-12 09:53:33 +08:00
Redkale
d5a41fc8b6 2021-01-10 14:59:00 +08:00
Redkale
c3c3afb3c3 2021-01-10 13:10:09 +08:00
Redkale
4f32dcda16 2021-01-10 10:33:48 +08:00
Redkale
67717e73ad 2021-01-10 10:19:05 +08:00
Redkale
1753734581 2021-01-10 09:05:47 +08:00
Redkale
3e408e535c 2021-01-09 15:41:35 +08:00
Redkale
f154292cb5 2021-01-09 15:32:59 +08:00
Redkale
ac261533c3 2021-01-09 14:53:12 +08:00
Redkale
1982c984ee 2021-01-09 14:42:45 +08:00
Redkale
fb5f82c44f 2021-01-09 14:01:41 +08:00
Redkale
840cea06db 2021-01-06 15:07:31 +08:00
Redkale
3237e2488e 2021-01-03 14:02:26 +08:00
Redkale
98e1e40e18 2021-01-03 14:00:54 +08:00
Redkale
def3fc7b2e 2021-01-03 12:04:51 +08:00
Redkale
6057b8f90a 2020-12-26 00:48:05 +08:00
Redkale
2847f761bb 修改MessageRecord的seqid生成规则 2020-12-26 00:07:49 +08:00
Redkale
b0a2d2ad80 2020-12-25 22:37:48 +08:00
Redkale
564b814f77 2020-12-25 20:51:18 +08:00
Redkale
433585a231 2020-12-25 20:33:50 +08:00
Redkale
6a4750e302 2020-12-25 20:30:47 +08:00
Redkale
748a6f11c1 2020-12-25 20:05:10 +08:00
Redkale
bdd9a82682 mq加入一些debug日志 2020-12-25 18:03:19 +08:00
Redkale
c551c157d1 @Local @AutoLoad(false) Service 能自动加载 2020-12-19 19:39:51 +08:00
Redkale
7e777229ba 2020-12-15 21:55:32 +08:00
Redkale
612bfcba37 WebSocket兼容connection:upgrade 小写的upgrade 2020-12-15 17:04:33 +08:00
Redkale
c44e00a100 2020-12-14 13:57:57 +08:00
Redkale
7f46b3194a Application 删除 restoreConfig 方法 2020-12-14 13:56:20 +08:00
Redkale
50b7999dee Application 增加 reloadConfig 方法 2020-12-14 13:55:19 +08:00
Redkale
871f84b1ff Redkale 2.3.0 开始 2020-12-14 13:49:47 +08:00
Redkale
4018a95af0 2020-12-13 12:17:00 +08:00
Redkale
ffe3ff5830 2020-12-13 01:08:09 +08:00
553 changed files with 46896 additions and 28884 deletions

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid* hs_err_pid*
/target/

View File

@@ -1,12 +1,12 @@
<h1>项目介绍</h1> <h1>项目介绍</h1>
<p> <p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 8全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 11全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。
</p> </p>
<strong>RedKale 有如下主要特点:</strong> <strong>RedKale 有如下主要特点:</strong>
<ol> <ol>
<li>大量使用Java 8新特性接口默认值、Stream、Lambda、JDk8内置的ASM等</li> <li>大量使用Java 8新特性接口默认值、Stream、Lambda、JDk8内置的ASM等</li>
<li>提供HTTP服务同时内置JSON功能与限时缓存功能</li> <li>提供HTTP服务同时内置JSON功能与限时缓存功能</li>
<li>TCP层完全使用NIO.2并统一TCP与UDP的接口换</li> <li>TCP层完全使用NIO并统一TCP与UDP的接口换</li>
<li>提供分布式与集中式部署的无缝切换</li> <li>提供分布式与集中式部署的无缝切换</li>
<li>提供类似JPA功能包含数据缓存自动同步、分表分库与简洁的数据层操作接口</li> <li>提供类似JPA功能包含数据缓存自动同步、分表分库与简洁的数据层操作接口</li>
<li>可以动态修改已依赖注入的资源</li> <li>可以动态修改已依赖注入的资源</li>

View File

@@ -6,7 +6,7 @@ APP_HOME=`dirname "$0"`
if [ ! -f "$APP_HOME"/conf/application.xml ]; then if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/.. APP_HOME="$APP_HOME"/..
fi fi
lib='.' lib='.'
for jar in `ls $APP_HOME/lib/*.jar` for jar in `ls $APP_HOME/lib/*.jar`

View File

@@ -4,5 +4,5 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" 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 java -server -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application

View File

@@ -24,5 +24,5 @@ done
export CLASSPATH=$CLASSPATH:$lib export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME" echo "$APP_HOME"
nohup java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out & nohup java -server -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out &

View File

@@ -1,14 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<application nodeid="10000" port="2121"> <application nodeid="10000" port="2020">
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
<resources> <resources>
</resources> </resources>
<server protocol="HTTP" port="6060"> <server protocol="HTTP" port="5050">
<request> <request>
<remoteaddr value="request.headers.X-RemoteAddress"/> <remoteaddr value="request.headers.X-RemoteAddress"/>
@@ -24,7 +22,7 @@
<filters autoload="true"/> <filters autoload="true"/>
<rest path="/pipes" /> <!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 --> <rest path="/pipes" />
<servlets path="/pipes" autoload="true" /> <servlets path="/pipes" autoload="true" />

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <persistence version="2.0">
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="" transaction-type="RESOURCE_LOCAL"> <persistence-unit name="" transaction-type="RESOURCE_LOCAL">
<shared-cache-mode>ALL</shared-cache-mode> <shared-cache-mode>ALL</shared-cache-mode>
@@ -25,7 +24,7 @@
<shared-cache-mode>ALL</shared-cache-mode> <shared-cache-mode>ALL</shared-cache-mode>
<properties> <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.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.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="1234"/> <property name="javax.persistence.jdbc.password" value="1234"/>
</properties> </properties>
</persistence-unit> </persistence-unit>

View File

@@ -1 +1 @@
<EFBFBD><EFBFBD><EFBFBD>н<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD> <EFBFBD><EFBFBD><EFBFBD>н<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>

View File

@@ -4,16 +4,33 @@
<groupId>org.redkale</groupId> <groupId>org.redkale</groupId>
<artifactId>redkale</artifactId> <artifactId>redkale</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>RedkaleProject</name>
<url>http://redkale.org</url> <url>http://redkale.org</url>
<description>redkale -- java framework</description> <description>redkale -- java framework</description>
<version>2.2.0</version> <version>2.5.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<licenses> <licenses>
<license> <license>
<name>Apache 2</name> <name>Apache 2</name>
<url>http://www.apache.org/licenses/</url> <url>https://www.apache.org/licenses/</url>
<distribution>repo</distribution> <distribution>repo</distribution>
<comments>Apache License</comments> <comments>Apache License</comments>
</license> </license>
</licenses> </licenses>
<developers> <developers>
@@ -21,13 +38,13 @@
<id>Redkale</id> <id>Redkale</id>
<name>redkale</name> <name>redkale</name>
<email>redkale@qq.com</email> <email>redkale@qq.com</email>
<url>http://redkale.org</url> <url>https://redkale.org</url>
<roles> <roles>
<role>Project Manager</role> <role>Project Manager</role>
<role>Architect</role> <role>Architect</role>
</roles> </roles>
<organization>redkale</organization> <organization>redkale</organization>
<organizationUrl>http://redkale.org</organizationUrl> <organizationUrl>https://redkale.org</organizationUrl>
<properties> <properties>
<dept>No</dept> <dept>No</dept>
</properties> </properties>
@@ -35,12 +52,6 @@
</developer> </developer>
</developers> </developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<name>Redkale</name>
<distributionManagement> <distributionManagement>
<snapshotRepository> <snapshotRepository>
<id>ossrh</id> <id>ossrh</id>
@@ -51,11 +62,13 @@
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository> </repository>
</distributionManagement> </distributionManagement>
<scm> <scm>
<url>https://github.com/redkale/redkale</url> <url>https://github.com/redkale/redkale</url>
<connection>scm:git:git@github.com/redkale/redkale.git</connection> <connection>scm:git:git@github.com/redkale/redkale.git</connection>
<developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection> <developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection>
</scm> </scm>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@@ -99,6 +112,7 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
@@ -111,6 +125,7 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>

View File

@@ -20,7 +20,8 @@
</profile> </profile>
<profile> <profile>
<id>release</id> <id>release</id>
<!--
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@@ -92,6 +93,7 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
-->
</profile> </profile>
</profiles> </profiles>
</settings> </settings>

138
pom.xml Normal file
View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.redkale</groupId>
<artifactId>redkale</artifactId>
<packaging>jar</packaging>
<name>RedkaleProject</name>
<url>https://redkale.org</url>
<description>redkale -- java framework</description>
<version>2.6.0</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-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>
</properties>
<licenses>
<license>
<name>Apache 2</name>
<url>https://www.apache.org/licenses/</url>
<distribution>repo</distribution>
<comments>Apache License</comments>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<developers>
<developer>
<id>Redkale</id>
<name>redkale</name>
<email>redkale@qq.com</email>
<url>https://redkale.org</url>
<roles>
<role>Project Manager</role>
<role>Architect</role>
</roles>
<organization>redkale</organization>
<organizationUrl>https://redkale.org</organizationUrl>
<properties>
<dept>No</dept>
</properties>
<timezone>8</timezone>
</developer>
</developers>
<scm>
<url>https://github.com/redkale/redkale</url>
<connection>scm:git:git@github.com/redkale/redkale.git</connection>
<developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<!-- 需要注释掉, 否则会生成native-image配置信息
<plugin>
<groupId>org.redkale.maven.plugins</groupId>
<artifactId>redkale-maven-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<executions>
<execution>
<id>redkale-compile</id>
<phase>process-classes</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
<manifest>
<mainClass>org.redkale.boot.Application</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1 @@

View File

@@ -33,6 +33,14 @@
--> -->
<resources> <resources>
<!--
【节点全局唯一】 @since 2.3.0
全局Serivce执行的线程池 Application.workExecutor, 没配置该节点将自动创建一个。
threads 线程数为0表示不启用workExecutor只用IO线程。默认: CPU核数, 核数=1的情况下默认值为2
hash: 是否使用ThreadHashExecutor作为线程池默认值为false
-->
<executor threads="4" hash="false"/>
<!-- <!--
【节点全局唯一】 【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。 transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
@@ -71,6 +79,7 @@
<!-- <!--
MQ管理接口配置 MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。 不同MQ节点所配置的MQ集群不能重复。
MQ跟着协议走所以mq的属性值需要被赋值在rest节点上, 由于SncpServlet是自动生成的故SNCP协议下mq属性值被赋值在service/services节点上
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线 name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
value 实现类名必须是org.redkale.mq.MessageAgent的子类 value 实现类名必须是org.redkale.mq.MessageAgent的子类
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置 MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
@@ -127,12 +136,12 @@
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。 如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
load: 加载文件,多个用;隔开。 load: 加载文件,多个用;隔开。
默认置入的system.property.的有: 默认置入的system.property.的有:
System.setProperty("net.transport.poolmaxconns", "100"); System.setProperty("redkale.net.transport.poolmaxconns", "100");
System.setProperty("net.transport.pinginterval", "30"); System.setProperty("redkale.net.transport.pinginterval", "30");
System.setProperty("net.transport.checkinterval", "30"); System.setProperty("redkale.net.transport.checkinterval", "30");
System.setProperty("convert.tiny", "true"); System.setProperty("redkale.convert.tiny", "true");
System.setProperty("convert.pool.size", "128"); System.setProperty("redkale.convert.pool.size", "128");
System.setProperty("convert.writer.buffer.defsize", "4096"); System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
<properties>节点下也可包含非<property>节点. <properties>节点下也可包含非<property>节点.
<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[] <property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
@@ -155,30 +164,38 @@
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
threads 线程数, 默认: CPU核数*2最小8个 threads【已废弃】 线程数, 默认: CPU核数*2最小8个【已废弃 @since 2.3.0】
maxconns 最大连接数, 小于1表示无限制 默认: 0 maxconns 最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
bufferPoolSize ByteBuffer池的大小默认: 线程数*4 bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2 responsePoolSize Response池的大小默认: 1024
aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive
readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时 readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时 writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
--> -->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib=""> <server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
<!-- <!--
【节点在<server>中唯一】 【节点在<server>中唯一】
value: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLCreator的子类 builder: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLBuilder的子类
clientauth: true/false/want sslProvider: java.security.Provider自定义的实现类如第三方: org.conscrypt.OpenSSLProvider、org.bouncycastle.jce.provider.BouncyCastleProvider
keystorepass: KEY密码 jsseProvider: java.security.Provider自定义的实现类如第三方: org.conscrypt.JSSEProvider、 org.bouncycastle.jce.provider.BouncyCastleJsseProvider
keystorefile: KEY文件 protocol: TLS版本默认值: TLS
truststorepass: TRUST密码 protocols: 设置setEnabledProtocols, 多个用,隔开 如: TLSv1.2,TLSv1.3
truststorefile: TRUST文件 clientAuth: WANT/NEED/NONE, 默认值: NONE
ciphers: 设置setEnabledCipherSuites, 多个用,隔开 如: TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256
keystorePass: KEY密码
keystoreFile: KEY文件 .jks
keystoreType: KEY类型 默认值为JKS
keystoreAlgorithm: KEY文件的algorithm 默认值为SunX509
truststorePass: TRUST密码
truststoreFile: TRUST文件
truststoreType: TRUST类型 默认值为JKS
truststoreAlgorithm: TRUST文件的algorithm 默认值为SunX509
--> -->
<ssl creator=""/> <ssl builder=""/>
<!-- <!--
加载所有的Service服务; 加载所有的Service服务;
@@ -199,6 +216,7 @@
<service value="com.xxx.XXX1Service"/> <service value="com.xxx.XXX1Service"/>
<!-- <!--
name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。 name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。
mq: 所属的MQ管理器当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
groups: 显式指定groups覆盖<services>节点的groups默认值。 groups: 显式指定groups覆盖<services>节点的groups默认值。
ignore: 是否禁用, 默认为false。 ignore: 是否禁用, 默认为false。
--> -->
@@ -286,7 +304,7 @@
period>0表示定时获取时间; 设置1000表示每秒刷新Date时间 period>0表示定时获取时间; 设置1000表示每秒刷新Date时间
--> -->
<response> <response>
<contenttype plain="text/plain; charset=utf-8" json="application/json; charset=utf-8"/> <content-type plain="text/plain; charset=utf-8" json="application/json; charset=utf-8"/>
<defcookie domain="" path=""/> <defcookie domain="" path=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" /> <addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/> <setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/>
@@ -298,8 +316,9 @@
【节点在<server>中唯一】 【节点在<server>中唯一】
当Server为HTTP协议时render才有效. 指定输出引擎的实现类 当Server为HTTP协议时render才有效. 指定输出引擎的实现类
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类 value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
suffixs: 引擎文件名后缀,多个用;隔开,默认值为: .htel
--> -->
<render value="org.redkalex.htel.HttpTemplateRender"/> <render value="org.redkalex.htel.HttpTemplateRender" suffixs=".htel"/>
<!-- <!--
【节点在<server>中唯一】 【节点在<server>中唯一】
当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点 当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点

View File

@@ -12,28 +12,18 @@
是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存, 非NONE字样统一视为ALL 是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存, 非NONE字样统一视为ALL
--> -->
<property name="javax.persistence.cachemode" value="ALL"/> <property name="javax.persistence.cachemode" value="ALL"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
<!-- <!--
javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。 是否自动建表当表不存在的时候, 目前只支持mysql、postgres 默认为false
为了兼容用户习惯Redkale内置常见JDBC驱动到javax.sql.DataSource的映射关系
org.mariadb.jdbc.Driver —————— org.mariadb.jdbc.MySQLDataSource
org.postgresql.Driver —————— org.postgresql.ds.PGConnectionPoolDataSource
com.mysql.jdbc.Driver —————— com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
com.mysql.cj.jdbc.Driver —————— com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
org.h2.Driver —————— org.h2.jdbcx.JdbcDataSource
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
并且如果JDBC驱动是以上几个版本javax.persistence.jdbc.driver属性都可以省略Redkale会根据javax.persistence.jdbc.url的值来识别驱动
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.source" value="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"/>
--> -->
<property name="javax.persistence.table.autoddl" value="false"/>
<!-- 多个URL用;隔开如分布式SearchSource需要配多个URL -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
<property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="123456"/> <property name="javax.persistence.jdbc.password" value="123456"/>
<!-- 最大连接数默认值CPU数*16 --> <!-- 最大连接数默认值CPU数 -->
<property name="javax.persistence.connections.limit" value="32"/> <property name="javax.persistence.connections.limit" value="12"/>
<!-- 包含的SQL模板相当于反向LIKE不同的JDBC驱动的SQL语句不一样Redkale内置了MySQL的语句 --> <!-- 包含的SQL模板相当于反向LIKE不同的JDBC驱动的SQL语句不一样Redkale内置了MySQL的语句 -->
<property name="javax.persistence.contain.sqltemplate" value="LOCATE(${keystr}, ${column}) > 0"/> <property name="javax.persistence.contain.sqltemplate" value="LOCATE(${keystr}, ${column}) > 0"/>
@@ -41,7 +31,7 @@
<!-- 复制表结构的SQL模板Redkale内置了MySQL的语句 --> <!-- 复制表结构的SQL模板Redkale内置了MySQL的语句 -->
<property name="javax.persistence.tablenotexist.sqlstates" value="42000;42S02"/> <property name="javax.persistence.tablenotexist.sqlstates" value="42000;42S02"/>
<property name="javax.persistence.tablecopy.sqltemplate" value="CREATE TABLE ${newtable} LIKE ${oldtable}"/> <property name="javax.persistence.tablecopy.sqltemplate" value="CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}"/>
</properties> </properties>
</persistence-unit> </persistence-unit>

View File

@@ -23,11 +23,17 @@ import java.lang.annotation.Target;
/** /**
* 值越大优先级越高 * 值越大优先级越高
* *
* @since Common Annotations 1.2 * @since Common Annotations 1.2
*/ */
@Target({ElementType.TYPE}) @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Priority { public @interface Priority {
/**
* 优先级值
*
* @return int
*/
int value(); int value();
} }

View File

@@ -16,17 +16,68 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Resource { public @interface Resource {
/**
* AuthenticationType
*/
@Deprecated
public enum AuthenticationType { public enum AuthenticationType {
/**
* @deprecated
*/
CONTAINER, CONTAINER,
/**
* @deprecated
*/
APPLICATION APPLICATION
} }
/**
* 资源名称
*
* @return String
*/
public String name() default ""; public String name() default "";
/**
* 依赖注入的类型
*
* @return Class
*/
public Class<?> type() default Object.class; public Class<?> type() default Object.class;
/**
*
* @return AuthenticationType
*/
@Deprecated
public AuthenticationType authenticationType() default AuthenticationType.CONTAINER; public AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
/**
*
* @return boolean
*/
@Deprecated
public boolean shareable() default true; public boolean shareable() default true;
/**
*
* @return String
*/
@Deprecated
public String description() default ""; public String description() default "";
/**
*
* @return String
*/
@Deprecated
public String mappedName() default ""; public String mappedName() default "";
/**
*
* @return String
*/
@Deprecated
public String lookup() default ""; public String lookup() default "";
} }

View File

@@ -51,4 +51,12 @@ public @interface Cacheable {
* @return int * @return int
*/ */
int interval() default 0; int interval() default 0;
/**
* DataSource是否直接返回对象的真实引用 而不是copy一份
*
* @return boolean
*/
boolean direct() default false;
} }

View File

@@ -81,12 +81,19 @@ public @interface Column {
boolean unique() default false; boolean unique() default false;
/** /**
* (Optional) Whether the database column is nullable. * (Optional) Whether the database column is required.
* *
* @return boolean * @return boolean
*/ */
boolean nullable() default true; boolean nullable() default true;
/**
* for OpenAPI Specification 3
*
* @return String
*/
String example() default "";
/** /**
* (Optional) Whether the column is included in SQL INSERT * (Optional) Whether the column is included in SQL INSERT
* statements generated by the persistence provider. * statements generated by the persistence provider.
@@ -109,11 +116,13 @@ public @interface Column {
* *
* @return String * @return String
*/ */
@Deprecated
String table() default ""; String table() default "";
/** /**
* (Optional) The column length. (Applies only if a * (Optional) The column length. (Applies only if a
* string-valued column is used.) * string-valued column is used.)
* if type==String and length == 65535 then sqltype is text
* *
* @return int * @return int
*/ */

View File

@@ -15,9 +15,7 @@
******************************************************************************/ ******************************************************************************/
package javax.persistence; package javax.persistence;
import java.lang.annotation.Target; import java.lang.annotation.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -27,6 +25,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* *
* @since Java Persistence 1.0 * @since Java Persistence 1.0
*/ */
@Inherited
@Documented @Documented
@Target(TYPE) @Target(TYPE)
@Retention(RUNTIME) @Retention(RUNTIME)

View File

@@ -82,6 +82,11 @@ public @interface Table {
*/ */
Index[] indexes() default {}; Index[] indexes() default {};
/**
* comment
*
* @return String
*/
String comment() default ""; String comment() default "";
} }

View File

@@ -4,26 +4,29 @@
* *
* @author zhangjx * @author zhangjx
* *
module org.redkale { */
module redkale {
requires java.base; requires java.base;
requires java.logging; requires java.logging;
requires java.xml; requires java.net.http;
requires java.sql; requires java.sql;
requires jdk.unsupported; //sun.misc.Unsafe requires jdk.unsupported; //sun.misc.Unsafe
exports javax.annotation; exports javax.annotation;
exports javax.persistence; exports javax.persistence;
exports org.redkale.asm;
exports org.redkale.boot; exports org.redkale.boot;
exports org.redkale.boot.watch; exports org.redkale.boot.watch;
exports org.redkale.cluster;
exports org.redkale.convert; exports org.redkale.convert;
exports org.redkale.convert.bson; exports org.redkale.convert.bson;
exports org.redkale.convert.ext; exports org.redkale.convert.ext;
exports org.redkale.convert.json; exports org.redkale.convert.json;
exports org.redkale.mq; exports org.redkale.mq;
exports org.redkale.net; exports org.redkale.net;
exports org.redkale.net.client;
exports org.redkale.net.http; exports org.redkale.net.http;
exports org.redkale.net.sncp; exports org.redkale.net.sncp;
exports org.redkale.service; exports org.redkale.service;
@@ -31,10 +34,11 @@ module org.redkale {
exports org.redkale.util; exports org.redkale.util;
exports org.redkale.watch; exports org.redkale.watch;
uses org.redkale.cluster.ClusterAgent;
uses org.redkale.mq.MessageAgent; uses org.redkale.mq.MessageAgent;
uses org.redkale.source.CacheSource; uses org.redkale.cluster.ClusterAgent;
uses org.redkale.source.SourceLoader; uses org.redkale.convert.ConvertProvider;
uses org.redkale.source.CacheSourceProvider;
uses org.redkale.source.DataSourceProvider;
uses org.redkale.util.ResourceInjectLoader; uses org.redkale.util.ResourceInjectLoader;
} }
*/

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java annotation. The methods of this class must be * A visitor to visit a Java annotation. The methods of this class must be
* called in the following order: ( <code>visit</code> | <code>visitEnum</code> | * called in the following order: ( &#60;tt&#62;visit&#60;/tt&#62; | &#60;tt&#62;visitEnum&#60;/tt&#62; |
* <code>visitAnnotation</code> | <code>visitArray</code> )* <code>visitEnd</code>. * &#60;tt&#62;visitAnnotation&#60;/tt&#62; | &#60;tt&#62;visitArray&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
* *
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
@@ -151,7 +151,7 @@ public abstract class AnnotationVisitor {
* @param desc * @param desc
* the class descriptor of the nested annotation class. * the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or * @return a visitor to visit the actual nested annotation value, or
* <code>null</code> if this visitor is not interested in visiting this * &#60;tt&#62;null&#60;/tt&#62; if this visitor is not interested in visiting this
* nested annotation. <i>The nested annotation value must be fully * nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation * visited before calling other methods on this annotation
* visitor</i>. * visitor</i>.
@@ -172,7 +172,7 @@ public abstract class AnnotationVisitor {
* @param name * @param name
* the value name. * the value name.
* @return a visitor to visit the actual array value elements, or * @return a visitor to visit the actual array value elements, or
* <code>null</code> if this visitor is not interested in visiting these * &#60;tt&#62;null&#60;/tt&#62; if this visitor is not interested in visiting these
* values. The 'name' parameters passed to the methods of this * values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited * visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>. * before calling other methods on this annotation visitor</i>.

View File

@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size; private int size;
/** /**
* <code>true<code> if values are named, <code>false</code> otherwise. Annotation * &#60;tt&#62;true&#60;tt&#62; if values are named, &#60;tt&#62;false&#60;/tt&#62; otherwise. Annotation
* writers used for annotation default and annotation arrays use unnamed * writers used for annotation default and annotation arrays use unnamed
* values. * values.
*/ */
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param cw * @param cw
* the class writer to which this annotation must be added. * the class writer to which this annotation must be added.
* @param named * @param named
* <code>true<code> if values are named, <code>false</code> otherwise. * &#60;tt&#62;true&#60;tt&#62; if values are named, &#60;tt&#62;false&#60;/tt&#62; otherwise.
* @param bv * @param bv
* where the annotation values must be stored. * where the annotation values must be stored.
* @param parent * @param parent
* where the number of annotation values must be stored. * where the number of annotation values must be stored.
* @param offset * @param offset
* where in <code>parent</code> the number of annotation values must * where in &#60;tt&#62;parent&#60;/tt&#62; the number of annotation values must
* be stored. * be stored.
*/ */
AnnotationWriter(final ClassWriter cw, final boolean named, AnnotationWriter(final ClassWriter cw, final boolean named,
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param out * @param out
* where the type reference and type path must be put. * where the type reference and type path must be put.
*/ */

View File

@@ -58,13 +58,15 @@
*/ */
package org.redkale.asm; package org.redkale.asm;
import java.util.Arrays;
/** /**
* A non standard class, field, method or code attribute. * A non standard class, field, method or code attribute.
* *
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
*/ */
class Attribute { public class Attribute {
/** /**
* The type of this attribute. * The type of this attribute.
@@ -77,7 +79,7 @@ class Attribute {
byte[] value; byte[] value;
/** /**
* The next attribute in this attribute list. May be <code>null</code>. * The next attribute in this attribute list. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
Attribute next; Attribute next;
@@ -92,19 +94,19 @@ class Attribute {
} }
/** /**
* Returns <code>true</code> if this type of attribute is unknown. The default * Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown. The default
* implementation of this method always returns <code>true</code>. * implementation of this method always returns &#60;tt&#62;true&#60;/tt&#62;.
* *
* @return <code>true</code> if this type of attribute is unknown. * @return &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown.
*/ */
public boolean isUnknown() { public boolean isUnknown() {
return true; return true;
} }
/** /**
* Returns <code>true</code> if this type of attribute is a code attribute. * Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is a code attribute.
* *
* @return <code>true</code> if this type of attribute is a code attribute. * @return &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is a code attribute.
*/ */
public boolean isCodeAttribute() { public boolean isCodeAttribute() {
return false; return false;
@@ -113,7 +115,7 @@ class Attribute {
/** /**
* Returns the labels corresponding to this attribute. * Returns the labels corresponding to this attribute.
* *
* @return the labels corresponding to this attribute, or <code>null</code> if * @return the labels corresponding to this attribute, or &#60;tt&#62;null&#60;/tt&#62; if
* this attribute is not a code attribute that contains labels. * this attribute is not a code attribute that contains labels.
*/ */
protected Label[] getLabels() { protected Label[] getLabels() {
@@ -123,7 +125,7 @@ class Attribute {
/** /**
* Reads a {@link #type type} attribute. This method must return a * Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type}, * <i>new</i> {@link Attribute} object, of type {@link #type type},
* corresponding to the <code>len</code> bytes starting at the given offset, in * corresponding to the &#60;tt&#62;len&#60;/tt&#62; bytes starting at the given offset, in
* the given class reader. * the given class reader.
* *
* @param cr * @param cr
@@ -146,7 +148,7 @@ class Attribute {
* containing the type and the length of the attribute, are not * containing the type and the length of the attribute, are not
* taken into account here. * taken into account here.
* @param labels * @param labels
* the labels of the method's code, or <code>null</code> if the * the labels of the method's code, or &#60;tt&#62;null&#60;/tt&#62; if the
* attribute to be read is not a code attribute. * attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given * @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes. * bytes.
@@ -169,11 +171,11 @@ class Attribute {
* class the items that corresponds to this attribute. * class the items that corresponds to this attribute.
* @param code * @param code
* the bytecode of the method corresponding to this code * the bytecode of the method corresponding to this code
* attribute, or <code>null</code> if this attribute is not a code * attribute, or &#60;tt&#62;null&#60;/tt&#62; if this attribute is not a code
* attributes. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to this * the length of the bytecode of the method corresponding to this
* code attribute, or <code>null</code> if this attribute is not a * code attribute, or &#60;tt&#62;null&#60;/tt&#62; if this attribute is not a
* code attribute. * code attribute.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to this * the maximum stack size of the method corresponding to this
@@ -216,11 +218,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method. * byte arrays, with the {@link #write write} method.
* @param code * @param code
* the bytecode of the method corresponding to these code * the bytecode of the method corresponding to these code
* attributes, or <code>null</code> if these attributes are not code * attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes are not code
* attributes. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to * the length of the bytecode of the method corresponding to
* these code attributes, or <code>null</code> if these attributes * these code attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes
* are not code attributes. * are not code attributes.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to these * the maximum stack size of the method corresponding to these
@@ -254,11 +256,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method. * byte arrays, with the {@link #write write} method.
* @param code * @param code
* the bytecode of the method corresponding to these code * the bytecode of the method corresponding to these code
* attributes, or <code>null</code> if these attributes are not code * attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes are not code
* attributes. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to * the length of the bytecode of the method corresponding to
* these code attributes, or <code>null</code> if these attributes * these code attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes
* are not code attributes. * are not code attributes.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to these * the maximum stack size of the method corresponding to these
@@ -281,4 +283,82 @@ class Attribute {
attr = attr.next; attr = attr.next;
} }
} }
//The stuff below is temporary - once proper support for nestmate attribute has been added, it can be safely removed.
//see also changes in ClassReader.accept.
/**
*
*/
public static class NestMembers extends Attribute {
/**
*
*/
public NestMembers() {
super("NestMembers");
}
byte[] bytes;
String[] classes;
@Override
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
int offset = off;
NestMembers a = new NestMembers();
int size = cr.readShort(off);
a.classes = new String[size];
off += 2;
for (int i = 0; i < size ; i++) {
a.classes[i] = cr.readClass(off, buf);
off += 2;
}
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
return a;
}
@Override
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
ByteVector v = new ByteVector(bytes.length);
v.putShort(classes.length);
for (String s : classes) {
v.putShort(cw.newClass(s));
}
return v;
}
}
/**
*
*/
public static class NestHost extends Attribute {
byte[] bytes;
String clazz;
/**
*
*/
public NestHost() {
super("NestHost");
}
@Override
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
int offset = off;
NestHost a = new NestHost();
a.clazz = cr.readClass(off, buf);
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
return a;
}
@Override
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
ByteVector v = new ByteVector(bytes.length);
v.putShort(cw.newClass(clazz));
return v;
}
}
static final Attribute[] DEFAULT_ATTRIBUTE_PROTOS = new Attribute[] {
new NestMembers(),
new NestHost()
};
} }

View File

@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary. * automatically enlarged if necessary.
* *
* @param b * @param b
* an array of bytes. May be <code>null</code> to put <code>len</code> * an array of bytes. May be &#60;tt&#62;null&#60;/tt&#62; to put &#60;tt&#62;len&#60;/tt&#62;
* null bytes into this byte vector. * null bytes into this byte vector.
* @param off * @param off
* index of the fist byte of b that must be copied. * index of the fist byte of b that must be copied.

View File

@@ -185,9 +185,9 @@ public class ClassReader {
public ClassReader(final byte[] b, final int off, final int len) { public ClassReader(final byte[] b, final int off, final int len) {
this.b = b; this.b = b;
// checks the class version // checks the class version
if (readShort(off + 6) > Opcodes.V10) { //if (readShort(off + 6) > Opcodes.V11) {
//throw new IllegalArgumentException(); // throw new IllegalArgumentException();
} //}
// parses the constant pool // parses the constant pool
items = new int[readUnsignedShort(off + 8)]; items = new int[readUnsignedShort(off + 8)];
int n = items.length; int n = items.length;
@@ -205,6 +205,10 @@ public class ClassReader {
case ClassWriter.FLOAT: case ClassWriter.FLOAT:
case ClassWriter.NAME_TYPE: case ClassWriter.NAME_TYPE:
case ClassWriter.INDY: case ClassWriter.INDY:
// @@@ ClassWriter.CONDY
// Enables MethodHandles.lookup().defineClass to function correctly
// when it reads the class name
case 17:
size = 5; size = 5;
break; break;
case ClassWriter.LONG: case ClassWriter.LONG:
@@ -267,7 +271,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). For interfaces, the * {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}. * super class is {@link Object}.
* *
* @return the internal name of super class, or <code>null</code> for * @return the internal name of super class, or &#60;tt&#62;null&#60;/tt&#62; for
* {@link Object} class. * {@link Object} class.
* *
* @see ClassVisitor#visit(int, int, String, String, String, String[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +285,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* *
* @return the array of internal names for all implemented interfaces or * @return the array of internal names for all implemented interfaces or
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
* *
* @see ClassVisitor#visit(int, int, String, String, String, String[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
*/ */
@@ -526,7 +530,7 @@ public class ClassReader {
* , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
*/ */
public void accept(final ClassVisitor classVisitor, final int flags) { public void accept(final ClassVisitor classVisitor, final int flags) {
accept(classVisitor, new Attribute[0], flags); accept(classVisitor, Attribute.DEFAULT_ATTRIBUTE_PROTOS, flags);
} }
/** /**
@@ -1932,7 +1936,7 @@ public class ClassReader {
* @param v * @param v
* start offset in {@link #b b} of the annotations to be read. * start offset in {@link #b b} of the annotations to be read.
* @param visible * @param visible
* <code>true</code> if the annotations to be read are visible at * &#60;tt&#62;true&#60;/tt&#62; if the annotations to be read are visible at
* runtime. * runtime.
*/ */
private void readParameterAnnotations(final MethodVisitor mv, private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2478,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account * and the length of the attribute, are not taken into account
* here. * here.
* @param labels * @param labels
* the labels of the method's code, or <code>null</code> if the * the labels of the method's code, or &#60;tt&#62;null&#60;/tt&#62; if the
* attribute to be read is not a code attribute. * attribute to be read is not a code attribute.
* @return the attribute that has been read, or <code>null</code> to skip this * @return the attribute that has been read, or &#60;tt&#62;null&#60;/tt&#62; to skip this
* attribute. * attribute.
*/ */
private Attribute readAttribute(final Attribute[] attrs, final String type, private Attribute readAttribute(final Attribute[] attrs, final String type,

View File

@@ -60,11 +60,11 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java class. The methods of this class must be called in * A visitor to visit a Java class. The methods of this class must be called in
* the following order: <code>visit</code> [ <code>visitSource</code> ] [ * the following order: &#60;tt&#62;visit&#60;/tt&#62; [ &#60;tt&#62;visitSource&#60;/tt&#62; ] [
* <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> | * &#60;tt&#62;visitModule&#60;/tt&#62; ][ &#60;tt&#62;visitOuterClass&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* ( * &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* (
* <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )* * &#60;tt&#62;visitInnerClass&#60;/tt&#62; | &#60;tt&#62;visitField&#60;/tt&#62; | &#60;tt&#62;visitMethod&#60;/tt&#62; )*
* <code>visitEnd</code>. * &#60;tt&#62;visitEnd&#60;/tt&#62;.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -120,18 +120,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see * the internal name of the class (see
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* @param signature * @param signature
* the signature of this class. May be <code>null</code> if the class * the signature of this class. May be &#60;tt&#62;null&#60;/tt&#62; if the class
* is not a generic one, and does not extend or implement generic * is not a generic one, and does not extend or implement generic
* classes or interfaces. * classes or interfaces.
* @param superName * @param superName
* the internal of name of the super class (see * the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For * {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be * interfaces, the super class is {@link Object}. May be
* <code>null</code>, but only for the {@link Object} class. * &#60;tt&#62;null&#60;/tt&#62;, but only for the {@link Object} class.
* @param interfaces * @param interfaces
* the internal names of the class's interfaces (see * the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
public void visit(int version, int access, String name, String signature, public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) { String superName, String[] interfaces) {
@@ -145,11 +145,11 @@ public abstract class ClassVisitor {
* *
* @param source * @param source
* the name of the source file from which the class was compiled. * the name of the source file from which the class was compiled.
* May be <code>null</code>. * May be &#60;tt&#62;null&#60;/tt&#62;.
* @param debug * @param debug
* additional debug information to compute the correspondance * additional debug information to compute the correspondance
* between source and compiled elements of the class. May be * between source and compiled elements of the class. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
public void visitSource(String source, String debug) { public void visitSource(String source, String debug) {
if (cv != null) { if (cv != null) {
@@ -166,7 +166,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}. * and {@code ACC_MANDATED}.
* @param version * @param version
* module version or null. * module version or null.
* @return a visitor to visit the module values, or <code>null</code> if * @return a visitor to visit the module values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this module. * this visitor is not interested in visiting this module.
*/ */
public ModuleVisitor visitModule(String name, int access, String version) { public ModuleVisitor visitModule(String name, int access, String version) {
@@ -187,11 +187,11 @@ public abstract class ClassVisitor {
* internal name of the enclosing class of the class. * internal name of the enclosing class of the class.
* @param name * @param name
* the name of the method that contains the class, or * the name of the method that contains the class, or
* <code>null</code> if the class is not enclosed in a method of its * &#60;tt&#62;null&#60;/tt&#62; if the class is not enclosed in a method of its
* enclosing class. * enclosing class.
* @param desc * @param desc
* the descriptor of the method that contains the class, or * the descriptor of the method that contains the class, or
* <code>null</code> if the class is not enclosed in a method of its * &#60;tt&#62;null&#60;/tt&#62; if the class is not enclosed in a method of its
* enclosing class. * enclosing class.
*/ */
public void visitOuterClass(String owner, String name, String desc) { public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +206,8 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,12 +231,12 @@ public abstract class ClassVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -269,10 +269,10 @@ public abstract class ClassVisitor {
* @param outerName * @param outerName
* the internal name of the class to which the inner class * the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}). * belongs (see {@link Type#getInternalName() getInternalName}).
* May be <code>null</code> for not member classes. * May be &#60;tt&#62;null&#60;/tt&#62; for not member classes.
* @param innerName * @param innerName
* the (simple) name of the inner class inside its enclosing * the (simple) name of the inner class inside its enclosing
* class. May be <code>null</code> for anonymous inner classes. * class. May be &#60;tt&#62;null&#60;/tt&#62; for anonymous inner classes.
* @param access * @param access
* the access flags of the inner class as originally declared in * the access flags of the inner class as originally declared in
* the enclosing class. * the enclosing class.
@@ -295,20 +295,20 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type Type}). * the field's descriptor (see {@link Type Type}).
* @param signature * @param signature
* the field's signature. May be <code>null</code> if the field's * the field's signature. May be &#60;tt&#62;null&#60;/tt&#62; if the field's
* type does not use generic types. * type does not use generic types.
* @param value * @param value
* the field's initial value. This parameter, which may be * the field's initial value. This parameter, which may be
* <code>null</code> if the field does not have an initial value, * &#60;tt&#62;null&#60;/tt&#62; if the field does not have an initial value,
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a * must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <code>int</code>, * {@link Double} or a {@link String} (for &#60;tt&#62;int&#60;/tt&#62;,
* <code>float</code>, <code>long</code> or <code>String</code> fields * &#60;tt&#62;float&#60;/tt&#62;, &#60;tt&#62;long&#60;/tt&#62; or &#60;tt&#62;String&#60;/tt&#62; fields
* respectively). <i>This parameter is only used for static * respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which * fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in * must be initialized through bytecode instructions in
* constructors or methods. * constructors or methods.
* @return a visitor to visit field annotations and attributes, or * @return a visitor to visit field annotations and attributes, or
* <code>null</code> if this class visitor is not interested in visiting * &#60;tt&#62;null&#60;/tt&#62; if this class visitor is not interested in visiting
* these annotations and attributes. * these annotations and attributes.
*/ */
public FieldVisitor visitField(int access, String name, String desc, public FieldVisitor visitField(int access, String name, String desc,
@@ -321,7 +321,7 @@ public abstract class ClassVisitor {
/** /**
* Visits a method of the class. This method <i>must</i> return a new * Visits a method of the class. This method <i>must</i> return a new
* {@link MethodVisitor} instance (or <code>null</code>) each time it is called, * {@link MethodVisitor} instance (or &#60;tt&#62;null&#60;/tt&#62;) each time it is called,
* i.e., it should not return a previously returned visitor. * i.e., it should not return a previously returned visitor.
* *
* @param access * @param access
@@ -333,14 +333,14 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type Type}). * the method's descriptor (see {@link Type Type}).
* @param signature * @param signature
* the method's signature. May be <code>null</code> if the method * the method's signature. May be &#60;tt&#62;null&#60;/tt&#62; if the method
* parameters, return type and exceptions do not use generic * parameters, return type and exceptions do not use generic
* types. * types.
* @param exceptions * @param exceptions
* the internal names of the method's exception classes (see * the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
* @return an object to visit the byte code of the method, or <code>null</code> * @return an object to visit the byte code of the method, or &#60;tt&#62;null&#60;/tt&#62;
* if this class visitor is not interested in visiting the code of * if this class visitor is not interested in visiting the code of
* this method. * this method.
*/ */

View File

@@ -391,8 +391,8 @@ public class ClassWriter extends ClassVisitor {
* A type table used to temporarily store internal names that will not * A type table used to temporarily store internal names that will not
* necessarily be stored in the constant pool. This type table is used by * necessarily be stored in the constant pool. This type table is used by
* the control flow and data flow analysis algorithm used to compute stack * the control flow and data flow analysis algorithm used to compute stack
* map frames from scratch. This array associates to each index <code>i</code> * map frames from scratch. This array associates to each index &#60;tt&#62;i&#60;/tt&#62;
* the Item whose index is <code>i</code>. All Item objects stored in this array * the Item whose index is &#60;tt&#62;i&#60;/tt&#62;. All Item objects stored in this array
* are also stored in the {@link #items} hash table. These two arrays allow * are also stored in the {@link #items} hash table. These two arrays allow
* to retrieve an Item from its index or, conversely, to get the index of an * to retrieve an Item from its index or, conversely, to get the index of an
* Item from its value. Each Item stores an internal name in its * Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute; private int compute;
/** /**
* <code>true</code> if some methods have wide forward jumps using ASM pseudo * &#60;tt&#62;true&#60;/tt&#62; if some methods have wide forward jumps using ASM pseudo
* instructions, which need to be expanded into sequences of standard * instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written * bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation. * with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1486,7 +1486,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @param itf
* <code>true</code> if <code>owner</code> is an interface. * &#60;tt&#62;true&#60;/tt&#62; if &#60;tt&#62;owner&#60;/tt&#62; is an interface.
* @return a new or already existing method reference item. * @return a new or already existing method reference item.
*/ */
Item newMethodItem(final String owner, final String name, Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1515,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @param itf
* <code>true</code> if <code>owner</code> is an interface. * &#60;tt&#62;true&#60;/tt&#62; if &#60;tt&#62;owner&#60;/tt&#62; is an interface.
* @return the index of a new or already existing method reference item. * @return the index of a new or already existing method reference item.
*/ */
public int newMethod(final String owner, final String name, public int newMethod(final String owner, final String name,
@@ -1778,7 +1778,7 @@ public class ClassWriter extends ClassVisitor {
* @param key * @param key
* a constant pool item. * a constant pool item.
* @return the constant pool's hash table item which is equal to the given * @return the constant pool's hash table item which is equal to the given
* item, or <code>null</code> if there is no such item. * item, or &#60;tt&#62;null&#60;/tt&#62; if there is no such item.
*/ */
private Item get(final Item key) { private Item get(final Item key) {
Item i = items[key.hashCode % items.length]; Item i = items[key.hashCode % items.length];

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java field. The methods of this class must be called in * A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <code>visitAnnotation</code> | * the following order: ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>. * &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -111,8 +111,8 @@ public abstract class FieldVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,12 +132,12 @@ public abstract class FieldVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value; private int value;
/** /**
* The runtime visible annotations of this field. May be <code>null</code>. * The runtime visible annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter anns; private AnnotationWriter anns;
/** /**
* The runtime invisible annotations of this field. May be <code>null</code>. * The runtime invisible annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter ianns; private AnnotationWriter ianns;
/** /**
* The runtime visible type annotations of this field. May be <code>null</code>. * The runtime visible type annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter tanns; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this field. May be * The runtime invisible type annotations of this field. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter itanns; private AnnotationWriter itanns;
/** /**
* The non standard attributes of this field. May be <code>null</code>. * The non standard attributes of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private Attribute attrs; private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type}). * the field's descriptor (see {@link Type}).
* @param signature * @param signature
* the field's signature. May be <code>null</code>. * the field's signature. May be &#60;tt&#62;null&#60;/tt&#62;.
* @param value * @param value
* the field's constant value. May be <code>null</code>. * the field's constant value. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
FieldWriter(final ClassWriter cw, final int access, final String name, FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) { final String desc, final String signature, final Object value) {

View File

@@ -1403,7 +1403,7 @@ class Frame {
/** /**
* Merges the input frame of the given basic block with the input and output * Merges the input frame of the given basic block with the input and output
* frames of this basic block. Returns <code>true</code> if the input frame of * frames of this basic block. Returns &#60;tt&#62;true&#60;/tt&#62; if the input frame of
* the given label has been changed by this operation. * the given label has been changed by this operation.
* *
* @param cw * @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge * @param edge
* the kind of the {@link Edge} between this label and 'label'. * the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}. * See {@link Edge#info}.
* @return <code>true</code> if the input frame of the given label has been * @return &#60;tt&#62;true&#60;/tt&#62; if the input frame of the given label has been
* changed by this operation. * changed by this operation.
*/ */
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) { final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
@@ -1511,7 +1511,7 @@ class Frame {
/** /**
* Merges the type at the given index in the given type array with the given * Merges the type at the given index in the given type array with the given
* type. Returns <code>true</code> if the type array has been modified by this * type. Returns &#60;tt&#62;true&#60;/tt&#62; if the type array has been modified by this
* operation. * operation.
* *
* @param cw * @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types. * an array of types.
* @param index * @param index
* the index of the type that must be merged in 'types'. * the index of the type that must be merged in 'types'.
* @return <code>true</code> if the type array has been modified by this * @return &#60;tt&#62;true&#60;/tt&#62; if the type array has been modified by this
* operation. * operation.
*/ */
private static boolean merge(final ClassWriter cw, int t, private static boolean merge(final ClassWriter cw, int t,

View File

@@ -99,7 +99,6 @@ public final class Handle {
*/ */
final boolean itf; final boolean itf;
/** /**
* Constructs a new field or method handle. * Constructs a new field or method handle.
* *

View File

@@ -82,7 +82,7 @@ class Handler {
/** /**
* Internal name of the type of exceptions handled by this handler, or * Internal name of the type of exceptions handled by this handler, or
* <code>null</code> to catch any exceptions. * &#60;tt&#62;null&#60;/tt&#62; to catch any exceptions.
*/ */
String desc; String desc;

View File

@@ -306,8 +306,8 @@ final class Item {
* @param i * @param i
* the item to be compared to this one. Both items must have the * the item to be compared to this one. Both items must have the
* same {@link #type}. * same {@link #type}.
* @return <code>true</code> if the given item if equal to this one, * @return &#60;tt&#62;true&#60;/tt&#62; if the given item if equal to this one,
* <code>false</code> otherwise. * &#60;tt&#62;false&#60;/tt&#62; otherwise.
*/ */
boolean isEqualTo(final Item i) { boolean isEqualTo(final Item i) {
switch (type) { switch (type) {

View File

@@ -325,8 +325,8 @@ public class Label {
* the position of first byte of the bytecode instruction that * the position of first byte of the bytecode instruction that
* contains this label. * contains this label.
* @param wideOffset * @param wideOffset
* <code>true</code> if the reference must be stored in 4 bytes, or * &#60;tt&#62;true&#60;/tt&#62; if the reference must be stored in 4 bytes, or
* <code>false</code> if it must be stored with 2 bytes. * &#60;tt&#62;false&#60;/tt&#62; if it must be stored with 2 bytes.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if this label has not been created by the given code writer. * if this label has not been created by the given code writer.
*/ */
@@ -389,7 +389,7 @@ public class Label {
* the position of this label in the bytecode. * the position of this label in the bytecode.
* @param data * @param data
* the bytecode of the method. * the bytecode of the method.
* @return <code>true</code> if a blank that was left for this label was too * @return &#60;tt&#62;true&#60;/tt&#62; if a blank that was left for this label was too
* small to store the offset. In such a case the corresponding jump * small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused * instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo * opcodes) using an unsigned two bytes offset. These pseudo

View File

@@ -5,7 +5,10 @@
*/ */
package org.redkale.asm; package org.redkale.asm;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import static org.redkale.asm.Opcodes.*;
/** /**
* MethodVisitor 的调试类 * MethodVisitor 的调试类
@@ -54,11 +57,20 @@ public class MethodDebugVisitor {
} }
} }
/**
*
* @param visitor MethodVisitor
*/
public MethodDebugVisitor(MethodVisitor visitor) { public MethodDebugVisitor(MethodVisitor visitor) {
//super(Opcodes.ASM5, visitor); //super(Opcodes.ASM5, visitor);
this.visitor = visitor; this.visitor = visitor;
} }
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
visitor.visitTryCatchBlock(start, end, handler, type);
if (debug) System.out.println("mv.visitTryCatchBlock(label0, label1, label2, \"" + type + "\");");
}
public AnnotationVisitor visitParameterAnnotation(int i, String string, boolean bln) { public AnnotationVisitor visitParameterAnnotation(int i, String string, boolean bln) {
AnnotationVisitor av = visitor.visitParameterAnnotation(i, string, bln); AnnotationVisitor av = visitor.visitParameterAnnotation(i, string, bln);
if (debug) System.out.println("mv.visitParameterAnnotation(" + i + ", \"" + string + "\", " + bln + ");"); if (debug) System.out.println("mv.visitParameterAnnotation(" + i + ", \"" + string + "\", " + bln + ");");
@@ -189,4 +201,74 @@ public class MethodDebugVisitor {
visitor.visitEnd(); visitor.visitEnd();
if (debug) System.out.println("mv.visitEnd();\r\n\r\n\r\n"); if (debug) System.out.println("mv.visitEnd();\r\n\r\n\r\n");
} }
public static void pushInt(MethodDebugVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, num);
} else if (num <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, num);
} else {
mv.visitLdcInsn(num);
}
}
public static void pushInt(MethodVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, num);
} else if (num <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, num);
} else {
mv.visitLdcInsn(num);
}
}
public static void visitAnnotation(final AnnotationVisitor av, final Annotation ann) {
try {
for (Method anm : ann.annotationType().getMethods()) {
final String mname = anm.getName();
if ("equals".equals(mname) || "hashCode".equals(mname) || "toString".equals(mname) || "annotationType".equals(mname)) continue;
final Object r = anm.invoke(ann);
if (r instanceof String[]) {
AnnotationVisitor av1 = av.visitArray(mname);
for (String item : (String[]) r) {
av1.visit(null, item);
}
av1.visitEnd();
} else if (r instanceof Class[]) {
AnnotationVisitor av1 = av.visitArray(mname);
for (Class item : (Class[]) r) {
av1.visit(null, Type.getType(item));
}
av1.visitEnd();
} else if (r instanceof Enum[]) {
AnnotationVisitor av1 = av.visitArray(mname);
for (Enum item : (Enum[]) r) {
av1.visitEnum(null, Type.getDescriptor(item.getClass()), ((Enum) item).name());
}
av1.visitEnd();
} else if (r instanceof Annotation[]) {
AnnotationVisitor av1 = av.visitArray(mname);
for (Annotation item : (Annotation[]) r) {
visitAnnotation(av1.visitAnnotation(null, Type.getDescriptor(((Annotation) item).annotationType())), item);
}
av1.visitEnd();
} else if (r instanceof Class) {
av.visit(mname, Type.getType((Class) r));
} else if (r instanceof Enum) {
av.visitEnum(mname, Type.getDescriptor(r.getClass()), ((Enum) r).name());
} else if (r instanceof Annotation) {
visitAnnotation(av.visitAnnotation(null, Type.getDescriptor(((Annotation) r).annotationType())), (Annotation) r);
} else {
av.visit(mname, r);
}
}
av.visitEnd();
} catch (Exception e) {
e.printStackTrace();
}
}
} }

View File

@@ -60,24 +60,24 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java method. The methods of this class must be called in * A visitor to visit a Java method. The methods of this class must be called in
* the following order: ( <code>visitParameter</code> )* [ * the following order: ( &#60;tt&#62;visitParameter&#60;/tt&#62; )* [
* <code>visitAnnotationDefault</code> ] ( <code>visitAnnotation</code> | * &#60;tt&#62;visitAnnotationDefault&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* <code>visitParameterAnnotation</code> <code>visitTypeAnnotation</code> | * &#60;tt&#62;visitParameterAnnotation&#60;/tt&#62; &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; |
* <code>visitAttribute</code> )* [ <code>visitCode</code> ( <code>visitFrame</code> | * &#60;tt&#62;visitAttribute&#60;/tt&#62; )* [ &#60;tt&#62;visitCode&#60;/tt&#62; ( &#60;tt&#62;visitFrame&#60;/tt&#62; |
* <code>visit<i>X</i>Insn</code> | <code>visitLabel</code> | * &#60;tt&#62;visit<i>X</i>Insn&#60;/tt&#62; | &#60;tt&#62;visitLabel&#60;/tt&#62; |
* <code>visitInsnAnnotation</code> | <code>visitTryCatchBlock</code> | * &#60;tt&#62;visitInsnAnnotation&#60;/tt&#62; | &#60;tt&#62;visitTryCatchBlock&#60;/tt&#62; |
* <code>visitTryCatchAnnotation</code> | <code>visitLocalVariable</code> | * &#60;tt&#62;visitTryCatchAnnotation&#60;/tt&#62; | &#60;tt&#62;visitLocalVariable&#60;/tt&#62; |
* <code>visitLocalVariableAnnotation</code> | <code>visitLineNumber</code> )* * &#60;tt&#62;visitLocalVariableAnnotation&#60;/tt&#62; | &#60;tt&#62;visitLineNumber&#60;/tt&#62; )*
* <code>visitMaxs</code> ] <code>visitEnd</code>. In addition, the * &#60;tt&#62;visitMaxs&#60;/tt&#62; ] &#60;tt&#62;visitEnd&#60;/tt&#62;. In addition, the
* <code>visit<i>X</i>Insn</code> and <code>visitLabel</code> methods must be called in * &#60;tt&#62;visit<i>X</i>Insn&#60;/tt&#62; and &#60;tt&#62;visitLabel&#60;/tt&#62; methods must be called in
* the sequential order of the bytecode instructions of the visited code, * the sequential order of the bytecode instructions of the visited code,
* <code>visitInsnAnnotation</code> must be called <i>after</i> the annotated * &#60;tt&#62;visitInsnAnnotation&#60;/tt&#62; must be called <i>after</i> the annotated
* instruction, <code>visitTryCatchBlock</code> must be called <i>before</i> the * instruction, &#60;tt&#62;visitTryCatchBlock&#60;/tt&#62; must be called <i>before</i> the
* labels passed as arguments have been visited, * labels passed as arguments have been visited,
* <code>visitTryCatchBlockAnnotation</code> must be called <i>after</i> the * &#60;tt&#62;visitTryCatchBlockAnnotation&#60;/tt&#62; must be called <i>after</i> the
* corresponding try catch block has been visited, and the * corresponding try catch block has been visited, and the
* <code>visitLocalVariable</code>, <code>visitLocalVariableAnnotation</code> and * &#60;tt&#62;visitLocalVariable&#60;/tt&#62;, &#60;tt&#62;visitLocalVariableAnnotation&#60;/tt&#62; and
* <code>visitLineNumber</code> methods must be called <i>after</i> the labels * &#60;tt&#62;visitLineNumber&#60;/tt&#62; methods must be called <i>after</i> the labels
* passed as arguments have been visited. * passed as arguments have been visited.
* *
* @author Eric Bruneton * @author Eric Bruneton
@@ -132,8 +132,8 @@ public abstract class MethodVisitor {
* @param name * @param name
* parameter name or null if none is provided. * parameter name or null if none is provided.
* @param access * @param access
* the parameter's access flags, only <code>ACC_FINAL</code>, * the parameter's access flags, only &#60;tt&#62;ACC_FINAL&#60;/tt&#62;,
* <code>ACC_SYNTHETIC</code> or/and <code>ACC_MANDATED</code> are * &#60;tt&#62;ACC_SYNTHETIC&#60;/tt&#62; or/and &#60;tt&#62;ACC_MANDATED&#60;/tt&#62; are
* allowed (see {@link Opcodes}). * allowed (see {@link Opcodes}).
*/ */
public void visitParameter(String name, int access) { public void visitParameter(String name, int access) {
@@ -146,7 +146,7 @@ public abstract class MethodVisitor {
* Visits the default value of this annotation interface method. * Visits the default value of this annotation interface method.
* *
* @return a visitor to the visit the actual default value of this * @return a visitor to the visit the actual default value of this
* annotation interface method, or <code>null</code> if this visitor is * annotation interface method, or &#60;tt&#62;null&#60;/tt&#62; if this visitor is
* not interested in visiting this default value. The 'name' * not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are * parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this * ignored. Moreover, exacly one visit method must be called on this
@@ -165,8 +165,8 @@ public abstract class MethodVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -193,12 +193,12 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -217,8 +217,8 @@ public abstract class MethodVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitParameterAnnotation(int parameter, public AnnotationVisitor visitParameterAnnotation(int parameter,
@@ -444,7 +444,6 @@ public abstract class MethodVisitor {
} }
} }
/** /**
* Visits a method instruction. A method instruction is an instruction that * Visits a method instruction. A method instruction is an instruction that
* invokes a method. * invokes a method.
@@ -569,7 +568,7 @@ public abstract class MethodVisitor {
* the constant to be loaded on the stack. This parameter must be * the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a * a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or * {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for <code>.class</code> constants, for classes whose * ARRAY sort for &#60;tt&#62;.class&#60;/tt&#62; constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a * version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for * {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0. * classes whose version is 51.0.
@@ -604,8 +603,8 @@ public abstract class MethodVisitor {
* @param dflt * @param dflt
* beginning of the default handler block. * beginning of the default handler block.
* @param labels * @param labels
* beginnings of the handler blocks. <code>labels[i]</code> is the * beginnings of the handler blocks. &#60;tt&#62;labels[i]&#60;/tt&#62; is the
* beginning of the handler block for the <code>min + i</code> key. * beginning of the handler block for the &#60;tt&#62;min + i&#60;/tt&#62; key.
*/ */
public void visitTableSwitchInsn(int min, int max, Label dflt, public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) { Label... labels) {
@@ -622,8 +621,8 @@ public abstract class MethodVisitor {
* @param keys * @param keys
* the values of the keys. * the values of the keys.
* @param labels * @param labels
* beginnings of the handler blocks. <code>labels[i]</code> is the * beginnings of the handler blocks. &#60;tt&#62;labels[i]&#60;/tt&#62; is the
* beginning of the handler block for the <code>keys[i]</code> key. * beginning of the handler block for the &#60;tt&#62;keys[i]&#60;/tt&#62; key.
*/ */
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) { if (mv != null) {
@@ -668,12 +667,12 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitInsnAnnotation(int typeRef, public AnnotationVisitor visitInsnAnnotation(int typeRef,
@@ -699,7 +698,7 @@ public abstract class MethodVisitor {
* beginning of the exception handler's code. * beginning of the exception handler's code.
* @param type * @param type
* internal name of the type of exceptions handled by the * internal name of the type of exceptions handled by the
* handler, or <code>null</code> to catch any exceptions (for * handler, or &#60;tt&#62;null&#60;/tt&#62; to catch any exceptions (for
* "finally" blocks). * "finally" blocks).
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if one of the labels has already been visited by this visitor * if one of the labels has already been visited by this visitor
@@ -725,12 +724,12 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
@@ -750,7 +749,7 @@ public abstract class MethodVisitor {
* the type descriptor of this local variable. * the type descriptor of this local variable.
* @param signature * @param signature
* the type signature of this local variable. May be * the type signature of this local variable. May be
* <code>null</code> if the local variable type does not use generic * &#60;tt&#62;null&#60;/tt&#62; if the local variable type does not use generic
* types. * types.
* @param start * @param start
* the first instruction corresponding to the scope of this local * the first instruction corresponding to the scope of this local
@@ -782,7 +781,7 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole. * &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param start * @param start
* the fist instructions corresponding to the continuous ranges * the fist instructions corresponding to the continuous ranges
* that make the scope of this local variable (inclusive). * that make the scope of this local variable (inclusive).
@@ -796,8 +795,8 @@ public abstract class MethodVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
@@ -819,7 +818,7 @@ public abstract class MethodVisitor {
* @param start * @param start
* the first instruction corresponding to this line number. * the first instruction corresponding to this line number.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if <code>start</code> has not already been visited by this * if &#60;tt&#62;start&#60;/tt&#62; has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method). * visitor (by the {@link #visitLabel visitLabel} method).
*/ */
public void visitLineNumber(int line, Label start) { public void visitLineNumber(int line, Label start) {

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions; int[] exceptions;
/** /**
* The annotation default attribute of this method. May be <code>null</code>. * The annotation default attribute of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private ByteVector annd; private ByteVector annd;
/** /**
* The runtime visible annotations of this method. May be <code>null</code>. * The runtime visible annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter anns; private AnnotationWriter anns;
/** /**
* The runtime invisible annotations of this method. May be <code>null</code>. * The runtime invisible annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter ianns; private AnnotationWriter ianns;
/** /**
* The runtime visible type annotations of this method. May be <code>null</code> * The runtime visible type annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;
* . * .
*/ */
private AnnotationWriter tanns; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this method. May be * The runtime invisible type annotations of this method. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter itanns; private AnnotationWriter itanns;
/** /**
* The runtime visible parameter annotations of this method. May be * The runtime visible parameter annotations of this method. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter[] panns; private AnnotationWriter[] panns;
/** /**
* The runtime invisible parameter annotations of this method. May be * The runtime invisible parameter annotations of this method. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter[] ipanns; private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset; private int lastCodeOffset;
/** /**
* The runtime visible type annotations of the code. May be <code>null</code>. * The runtime visible type annotations of the code. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter ctanns; private AnnotationWriter ctanns;
/** /**
* The runtime invisible type annotations of the code. May be <code>null</code>. * The runtime invisible type annotations of the code. May be &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter ictanns; private AnnotationWriter ictanns;
@@ -446,7 +446,7 @@ class MethodWriter extends MethodVisitor {
* is relative to the beginning of the current basic block, i.e., the true * is relative to the beginning of the current basic block, i.e., the true
* stack size after the last visited instruction is equal to the * stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block * {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <code>stackSize</code>. * plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/ */
private int stackSize; private int stackSize;
@@ -455,7 +455,7 @@ class MethodWriter extends MethodVisitor {
* This size is relative to the beginning of the current basic block, i.e., * This size is relative to the beginning of the current basic block, i.e.,
* the true maximum stack size after the last visited instruction is equal * the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic * to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <code>stackSize</code>. * block plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/ */
private int maxStackSize; private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type}). * the method's descriptor (see {@link Type}).
* @param signature * @param signature
* the method's signature. May be <code>null</code>. * the method's signature. May be &#60;tt&#62;null&#60;/tt&#62;.
* @param exceptions * @param exceptions
* the internal names of the method's exceptions. May be * the internal names of the method's exceptions. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
* @param compute * @param compute
* Indicates what must be automatically computed (see #compute). * Indicates what must be automatically computed (see #compute).
*/ */

View File

@@ -60,9 +60,9 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java module. The methods of this class must be called in * A visitor to visit a Java module. The methods of this class must be called in
* the following order: <code>visitMainClass</code> | ( <code>visitPackage</code> | * the following order: &#60;tt&#62;visitMainClass&#60;/tt&#62; | ( &#60;tt&#62;visitPackage&#60;/tt&#62; |
* <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> | * &#60;tt&#62;visitRequire&#60;/tt&#62; | &#60;tt&#62;visitExport&#60;/tt&#62; | &#60;tt&#62;visitOpen&#60;/tt&#62; |
* <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>. * &#60;tt&#62;visitUse&#60;/tt&#62; | &#60;tt&#62;visitProvide&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
* *
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)}, * The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)} * {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to * @param modules the qualified names of the modules that can access to
* the public classes of the exported package or * the public classes of the exported package or
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
public void visitExport(String packaze, int access, String... modules) { public void visitExport(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep * @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or * reflection to the classes of the open package or
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
public void visitOpen(String packaze, int access, String... modules) { public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {

View File

@@ -73,19 +73,23 @@ package org.redkale.asm;
public interface Opcodes { public interface Opcodes {
// ASM API versions // ASM API versions
int ASM4 = 4 << 16 | 0 << 8;
int ASM4 = 4 << 16 | 0 << 8 | 0; int ASM5 = 5 << 16 | 0 << 8;
int ASM5 = 5 << 16 | 0 << 8 | 0; int ASM6 = 6 << 16 | 0 << 8;
int ASM6 = 6 << 16 | 0 << 8 | 0;
// versions // versions
int V1_1 = 3 << 16 | 45;
int V1_2 = 0 << 16 | 46;
int V1_3 = 0 << 16 | 47;
int V1_4 = 0 << 16 | 48;
int V1_5 = 0 << 16 | 49; int V1_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50; int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51; int V1_7 = 0 << 16 | 51;
int V1_8 = 0 << 16 | 52; int V1_8 = 0 << 16 | 52;
int V9 = 0 << 16 | 53; int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54; int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
// access flags // access flags

View File

@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
public class Type { public class Type {
/** /**
* The sort of the <code>void</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;void&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int VOID = 0; public static final int VOID = 0;
/** /**
* The sort of the <code>boolean</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;boolean&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int BOOLEAN = 1; public static final int BOOLEAN = 1;
/** /**
* The sort of the <code>char</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;char&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int CHAR = 2; public static final int CHAR = 2;
/** /**
* The sort of the <code>byte</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;byte&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int BYTE = 3; public static final int BYTE = 3;
/** /**
* The sort of the <code>short</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;short&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int SHORT = 4; public static final int SHORT = 4;
/** /**
* The sort of the <code>int</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;int&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int INT = 5; public static final int INT = 5;
/** /**
* The sort of the <code>float</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;float&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int FLOAT = 6; public static final int FLOAT = 6;
/** /**
* The sort of the <code>long</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;long&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int LONG = 7; public static final int LONG = 7;
/** /**
* The sort of the <code>double</code> type. See {@link #getSort getSort}. * The sort of the &#60;tt&#62;double&#60;/tt&#62; type. See {@link #getSort getSort}.
*/ */
public static final int DOUBLE = 8; public static final int DOUBLE = 8;
@@ -131,55 +131,55 @@ public class Type {
public static final int METHOD = 11; public static final int METHOD = 11;
/** /**
* The <code>void</code> type. * The &#60;tt&#62;void&#60;/tt&#62; type.
*/ */
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1); | (5 << 16) | (0 << 8) | 0, 1);
/** /**
* The <code>boolean</code> type. * The &#60;tt&#62;boolean&#60;/tt&#62; type.
*/ */
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1); | (0 << 16) | (5 << 8) | 1, 1);
/** /**
* The <code>char</code> type. * The &#60;tt&#62;char&#60;/tt&#62; type.
*/ */
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1); | (0 << 16) | (6 << 8) | 1, 1);
/** /**
* The <code>byte</code> type. * The &#60;tt&#62;byte&#60;/tt&#62; type.
*/ */
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1); | (0 << 16) | (5 << 8) | 1, 1);
/** /**
* The <code>short</code> type. * The &#60;tt&#62;short&#60;/tt&#62; type.
*/ */
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1); | (0 << 16) | (7 << 8) | 1, 1);
/** /**
* The <code>int</code> type. * The &#60;tt&#62;int&#60;/tt&#62; type.
*/ */
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1); | (0 << 16) | (0 << 8) | 1, 1);
/** /**
* The <code>float</code> type. * The &#60;tt&#62;float&#60;/tt&#62; type.
*/ */
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1); | (2 << 16) | (2 << 8) | 1, 1);
/** /**
* The <code>long</code> type. * The &#60;tt&#62;long&#60;/tt&#62; type.
*/ */
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1); | (1 << 16) | (1 << 8) | 2, 1);
/** /**
* The <code>double</code> type. * The &#60;tt&#62;double&#60;/tt&#62; type.
*/ */
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1); | (3 << 16) | (3 << 8) | 2, 1);
@@ -439,8 +439,8 @@ public class Type {
* @return the size of the arguments of the method (plus one for the * @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return * implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i = * value, retSize, packed into a single int i =
* <code>(argSize &lt;&lt; 2) | retSize</code> (argSize is therefore equal to * &#60;tt&#62;(argSize &lt;&lt; 2) | retSize&#60;/tt&#62; (argSize is therefore equal to
* <code>i &gt;&gt; 2</code>, and retSize to <code>i &amp; 0x03</code>). * &#60;tt&#62;i &gt;&gt; 2&#60;/tt&#62;, and retSize to &#60;tt&#62;i &amp; 0x03&#60;/tt&#62;).
*/ */
public static int getArgumentsAndReturnSizes(final String desc) { public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1; int n = 1;
@@ -645,9 +645,9 @@ public class Type {
* @return the size of the arguments (plus one for the implicit this * @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize, * argument), argSize, and the size of the return value, retSize,
* packed into a single * packed into a single
* int i = <code>(argSize &lt;&lt; 2) | retSize</code> * int i = &#60;tt&#62;(argSize &lt;&lt; 2) | retSize&#60;/tt&#62;
* (argSize is therefore equal to <code>i &gt;&gt; 2</code>, * (argSize is therefore equal to &#60;tt&#62;i &gt;&gt; 2&#60;/tt&#62;,
* and retSize to <code>i &amp; 0x03</code>). * and retSize to &#60;tt&#62;i &amp; 0x03&#60;/tt&#62;).
*/ */
public int getArgumentsAndReturnSizes() { public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor()); return getArgumentsAndReturnSizes(getDescriptor());
@@ -838,8 +838,8 @@ public class Type {
* Returns the size of values of this type. This method must not be used for * Returns the size of values of this type. This method must not be used for
* method types. * method types.
* *
* @return the size of values of this type, i.e., 2 for <code>long</code> and * @return the size of values of this type, i.e., 2 for &#60;tt&#62;long&#60;/tt&#62; and
* <code>double</code>, 0 for <code>void</code> and 1 otherwise. * &#60;tt&#62;double&#60;/tt&#62;, 0 for &#60;tt&#62;void&#60;/tt&#62; and 1 otherwise.
*/ */
public int getSize() { public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null) // the size is in byte 0 of 'off' for primitive types (buf == null)
@@ -855,8 +855,8 @@ public class Type {
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
* @return an opcode that is similar to the given opcode, but adapted to * @return an opcode that is similar to the given opcode, but adapted to
* this Java type. For example, if this type is <code>float</code> and * this Java type. For example, if this type is &#60;tt&#62;float&#60;/tt&#62; and
* <code>opcode</code> is IRETURN, this method returns FRETURN. * &#60;tt&#62;opcode&#60;/tt&#62; is IRETURN, this method returns FRETURN.
*/ */
public int getOpcode(final int opcode) { public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
@@ -879,7 +879,7 @@ public class Type {
* *
* @param o * @param o
* the object to be compared to this type. * the object to be compared to this type.
* @return <code>true</code> if the given object is equal to this type. * @return &#60;tt&#62;true&#60;/tt&#62; if the given object is equal to this type.
*/ */
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {

View File

@@ -161,7 +161,7 @@ public class TypePath {
* @return the corresponding TypePath object, or null if the path is empty. * @return the corresponding TypePath object, or null if the path is empty.
*/ */
public static TypePath fromString(final String typePath) { public static TypePath fromString(final String typePath) {
if (typePath == null || typePath.length() == 0) { if (typePath == null || typePath.isEmpty()) {
return null; return null;
} }
int n = typePath.length(); int n = typePath.length();

View File

@@ -0,0 +1,535 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.io.*;
import java.lang.reflect.*;
import java.math.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import javax.persistence.*;
import org.redkale.convert.*;
import org.redkale.convert.json.*;
import org.redkale.mq.MessageMultiConsumer;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.source.*;
import org.redkale.util.*;
/**
* API接口文档生成类作用生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br>
* 继承 HttpBaseServlet 是为了获取 HttpMapping 信息 <br>
* https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public final class ApiDocsService {
private static final java.lang.reflect.Type TYPE_RETRESULT_OBJECT = new TypeToken<RetResult<Object>>() {
}.getType();
private static final java.lang.reflect.Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
}.getType();
private static final java.lang.reflect.Type TYPE_RETRESULT_INTEGER = new TypeToken<RetResult<Integer>>() {
}.getType();
private static final java.lang.reflect.Type TYPE_RETRESULT_LONG = new TypeToken<RetResult<Long>>() {
}.getType();
private final Application app; //Application全局对象
public ApiDocsService(Application app) {
this.app = app;
}
public void run(String[] args) throws Exception {
//是否跳过RPC接口
final boolean skipRPC = Arrays.toString(args).toLowerCase().contains("skip-rpc") && !Arrays.toString(args).toLowerCase().contains("skip-rpc=false");
List<Map> serverList = new ArrayList<>();
Field __prefix = HttpServlet.class.getDeclaredField("_prefix");
__prefix.setAccessible(true);
Map<String, Map<String, Map<String, Object>>> typesMap = new LinkedHashMap<>();
//https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
Map<String, Object> swaggerPathsMap = new LinkedHashMap<>();
List<Map> swaggerServers = new ArrayList<>();
List<Map> swaggerTags = new ArrayList<>();
Map<String, Map<String, Object>> swaggerComponentsMap = new LinkedHashMap<>();
for (NodeServer node : app.servers) {
if (!(node instanceof NodeHttpServer)) continue;
final Map<String, Object> map = new LinkedHashMap<>();
serverList.add(map);
HttpServer server = node.getServer();
map.put("address", server.getSocketAddress());
swaggerServers.add(Utility.ofMap("url", "http://localhost:" + 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()) {
if (!(servlet instanceof HttpServlet)) continue;
if (servlet instanceof WebSocketServlet) continue;
if (servlet.getClass().getAnnotation(MessageMultiConsumer.class) != null) {
node.logger.log(Level.INFO, servlet + " be skipped because has @MessageMultiConsumer");
continue;
}
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws == null) {
node.logger.log(Level.WARNING, servlet + " not found @WebServlet");
continue;
}
if (ws.name().isEmpty()) {
node.logger.log(Level.INFO, servlet + " be skipped because @WebServlet.name is empty");
continue;
}
final String tag = ws.name().isEmpty() ? servlet.getClass().getSimpleName().replace("Servlet", "").toLowerCase() : ws.name();
final Map<String, Object> servletMap = new LinkedHashMap<>();
String prefix = (String) __prefix.get(servlet);
String[] urlregs = ws.value();
if (prefix != null && !prefix.isEmpty()) {
for (int i = 0; i < urlregs.length; i++) {
urlregs[i] = prefix + urlregs[i];
}
}
servletMap.put("urlregs", urlregs);
servletMap.put("moduleid", ws.moduleid());
servletMap.put("name", ws.name());
servletMap.put("comment", ws.comment());
List<Map> mappingsList = new ArrayList<>();
servletMap.put("mappings", mappingsList);
final Class selfClz = servlet.getClass();
Class clz = servlet.getClass();
HashSet<String> actionUrls = new HashSet<>();
do {
if (Modifier.isAbstract(clz.getModifiers())) break;
for (Method method : clz.getMethods()) {
if (method.getParameterCount() != 2) continue;
HttpMapping action = method.getAnnotation(HttpMapping.class);
if (action == null) continue;
if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法
if (actionUrls.contains(action.url())) continue;
if (HttpScope.class.isAssignableFrom(action.result())) continue; //忽略模板引擎的方法
if (action.rpconly() && skipRPC) continue; //不生成RPC接口
final List<Map<String, Object>> swaggerParamsList = new ArrayList<>();
final Map<String, Object> mappingMap = new LinkedHashMap<>();
mappingMap.put("url", prefix + action.url());
actionUrls.add(action.url());
mappingMap.put("auth", action.auth());
mappingMap.put("actionid", action.actionid());
mappingMap.put("comment", action.comment());
List<Map> paramsList = new ArrayList<>();
mappingMap.put("params", paramsList);
List<String> results = new ArrayList<>();
Type resultType = action.result();
if (!action.resultref().isEmpty()) {
Field f = servlet.getClass().getDeclaredField(action.resultref());
f.setAccessible(true);
resultType = (Type) f.get(servlet);
}
// for (final Class rtype : action.results()) {
// results.add(rtype.getName());
// if (typesMap.containsKey(rtype.getName())) continue;
// if (rtype.getName().startsWith("java.")) continue;
// if (rtype.getName().startsWith("javax.")) continue;
// final boolean filter = FilterBean.class.isAssignableFrom(rtype);
// final Map<String, Map<String, Object>> typeMap = new LinkedHashMap<>();
// Class loop = rtype;
// do {
// if (loop == null || loop.isInterface()) break;
// for (Field field : loop.getDeclaredFields()) {
// if (Modifier.isFinal(field.getModifiers())) continue;
// if (Modifier.isStatic(field.getModifiers())) continue;
//
// Map<String, Object> fieldmap = new LinkedHashMap<>();
// fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
//
// Comment comment = field.getAnnotation(Comment.class);
// Column col = field.getAnnotation(Column.class);
// FilterColumn fc = field.getAnnotation(FilterColumn.class);
// if (comment != null) {
// fieldmap.put("comment", comment.value());
// } else if (col != null) {
// fieldmap.put("comment", col.comment());
// } else if (fc != null) {
// fieldmap.put("comment", fc.comment());
// }
// fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
// fieldmap.put("updatable", (filter || col == null || col.updatable()));
// if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) {
// if (field.getAnnotation(RestAddress.class) != null) continue;
// }
//
// typeMap.put(field.getName(), fieldmap);
// }
// } while ((loop = loop.getSuperclass()) != Object.class);
// typesMap.put(rtype.getName(), typeMap);
// }
mappingMap.put("results", results);
boolean hasbodyparam = false;
Map<String, Object> swaggerRequestBody = new LinkedHashMap<>();
for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) {
final Map<String, Object> oldapisParamMap = new LinkedHashMap<>();
final boolean isarray = param.type().isArray();
final Class ptype = isarray ? param.type().getComponentType() : param.type();
oldapisParamMap.put("name", param.name());
oldapisParamMap.put("radix", param.radix());
oldapisParamMap.put("type", ptype.getName() + (isarray ? "[]" : ""));
oldapisParamMap.put("style", param.style());
oldapisParamMap.put("comment", param.comment());
oldapisParamMap.put("required", param.required());
paramsList.add(oldapisParamMap);
{
final Map<String, Object> paramSchemaMap = new LinkedHashMap<>();
Type paramGenericType = param.type();
if (!param.typeref().isEmpty()) {
Field f = servlet.getClass().getDeclaredField(param.typeref());
f.setAccessible(true);
paramGenericType = (Type) f.get(servlet);
}
simpleSchemaType(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)));
} else {
final Map<String, Object> swaggerParamMap = new LinkedHashMap<>();
swaggerParamMap.put("name", param.name());
swaggerParamMap.put("in", param.style().name().toLowerCase());
swaggerParamMap.put("description", param.comment());
swaggerParamMap.put("required", param.required());
if (param.deprecated()) {
swaggerParamMap.put("deprecated", param.deprecated());
}
//https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameterStyle
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()) {
swaggerParamMap.put("example", param.example());
}
swaggerParamsList.add(swaggerParamMap);
}
}
if (param.style() == HttpParam.HttpParameterStyle.BODY) hasbodyparam = true;
if (ptype.isPrimitive() || ptype == String.class) continue;
if (typesMap.containsKey(ptype.getName())) continue;
if (ptype.getName().startsWith("java.")) continue;
if (ptype.getName().startsWith("javax.")) continue;
final Map<String, Map<String, Object>> typeMap = new LinkedHashMap<>();
Class loop = ptype;
final boolean filter = FilterBean.class.isAssignableFrom(loop);
do {
if (loop == null || loop.isInterface()) break;
for (Field field : loop.getDeclaredFields()) {
if (Modifier.isFinal(field.getModifiers())) continue;
if (Modifier.isStatic(field.getModifiers())) continue;
Map<String, Object> fieldmap = new LinkedHashMap<>();
fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
Column col = field.getAnnotation(Column.class);
FilterColumn fc = field.getAnnotation(FilterColumn.class);
Comment comment = field.getAnnotation(Comment.class);
if (comment != null) {
fieldmap.put("comment", comment.value());
} else if (col != null) {
fieldmap.put("comment", col.comment());
} else if (fc != null) {
fieldmap.put("comment", fc.comment());
}
fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
fieldmap.put("updatable", (filter || col == null || col.updatable()));
if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) {
if (field.getAnnotation(RestAddress.class) != null) continue;
}
typeMap.put(field.getName(), fieldmap);
}
} while ((loop = loop.getSuperclass()) != Object.class);
typesMap.put(ptype.getName(), typeMap);
}
mappingMap.put("result", action.result().getSimpleName().replace("void", "Object"));
mappingsList.add(mappingMap);
final Map<String, Object> swaggerOperatMap = new LinkedHashMap<>();
swaggerOperatMap.put("tags", new String[]{tag});
swaggerOperatMap.put("operationId", action.name());
if (method.getAnnotation(Deprecated.class) != null) {
swaggerOperatMap.put("deprecated", true);
}
Map<String, Object> respSchemaMap = new LinkedHashMap<>();
simpleSchemaType(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);
if (!swaggerRequestBody.isEmpty()) swaggerOperatMap.put("requestBody", swaggerRequestBody);
swaggerOperatMap.put("parameters", swaggerParamsList);
String actiondesc = action.comment();
if (action.rpconly()) actiondesc = "[Only for RPC API] " + actiondesc;
swaggerOperatMap.put("responses", Utility.ofMap("200", Utility.ofMap("description", actiondesc, "content", Utility.ofMap("application/json", respMap))));
String m = action.methods() == null || action.methods().length == 0 ? null : action.methods()[0].toLowerCase();
if (m == null) {
m = hasbodyparam || TYPE_RETRESULT_STRING.equals(resultType) || TYPE_RETRESULT_INTEGER.equals(resultType)
|| TYPE_RETRESULT_LONG.equals(resultType) || action.name().contains("create") || action.name().contains("insert")
|| action.name().contains("update") || action.name().contains("delete") || action.name().contains("send") ? "post" : "get";
}
swaggerPathsMap.put(prefix + action.url(), Utility.ofMap("description", action.comment(), m, swaggerOperatMap));
}
} while ((clz = clz.getSuperclass()) != HttpServlet.class);
mappingsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url")));
servletsList.add(servletMap);
if (!actionUrls.isEmpty()) swaggerTags.add(Utility.ofMap("name", tag, "description", ws.comment()));
}
servletsList.sort((o1, o2) -> {
String[] urlregs1 = (String[]) o1.get("urlregs");
String[] urlregs2 = (String[]) o2.get("urlregs");
return urlregs1.length > 0 ? (urlregs2.length > 0 ? urlregs1[0].compareTo(urlregs2[0]) : 1) : -1;
});
}
{ // https://github.com/OAI/OpenAPI-Specification
Map<String, Object> swaggerResultMap = new LinkedHashMap<>();
swaggerResultMap.put("openapi", "3.0.0");
Map<String, Object> infomap = new LinkedHashMap<>();
infomap.put("title", "Redkale generate apidoc");
infomap.put("version", "1.0.0");
swaggerResultMap.put("info", infomap);
swaggerResultMap.put("servers", swaggerServers);
swaggerResultMap.put("paths", swaggerPathsMap);
swaggerResultMap.put("tags", swaggerTags);
if (!swaggerComponentsMap.isEmpty()) swaggerResultMap.put("components", Utility.ofMap("schemas", swaggerComponentsMap));
final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "openapi-doc.json"));
out.write(JsonConvert.root().convertTo(swaggerResultMap).getBytes(StandardCharsets.UTF_8));
out.close();
}
{
Map<String, Object> oldapisResultMap = new LinkedHashMap<>();
oldapisResultMap.put("servers", serverList);
oldapisResultMap.put("types", typesMap);
final String json = JsonConvert.root().convertTo(oldapisResultMap);
final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json"));
out.write(json.getBytes(StandardCharsets.UTF_8));
out.close();
File doctemplate = new File(app.getConfPath().toString(), "apidoc-template.html");
InputStream in = null;
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();
}
}
private static void simpleSchemaType(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");
} else if (type == long.class || type == Long.class
|| type == AtomicLong.class || type == LongAdder.class || type == BigInteger.class) {
schemaMap.put("type", "integer");
schemaMap.put("format", "int64");
} else if (type == float.class || type == Float.class) {
schemaMap.put("type", "number");
schemaMap.put("format", "float");
} else if (type == double.class || type == Double.class || type == BigDecimal.class) {
schemaMap.put("type", "number");
schemaMap.put("format", "double");
} else if (type == boolean.class || type == Boolean.class || type == AtomicBoolean.class) {
schemaMap.put("type", "boolean");
} else if (type.isPrimitive() || Number.class.isAssignableFrom(type)) {
schemaMap.put("type", "number");
} else if (type == String.class || CharSequence.class.isAssignableFrom(type)) {
schemaMap.put("type", "string");
} else if (recursive && (type.isArray() || Collection.class.isAssignableFrom(type))) {
schemaMap.put("type", "array");
Map<String, Object> sbumap = new LinkedHashMap<>();
if (type.isArray()) {
simpleSchemaType(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);
} else if (subpt instanceof ParameterizedType && ((ParameterizedType) subpt).getOwnerType() instanceof Class) {
simpleSchemaType(logger, componentsMap, (Class) ((ParameterizedType) subpt).getOwnerType(), subpt, sbumap, false);
} else {
sbumap.put("type", "object");
}
} else {
sbumap.put("type", "object");
}
schemaMap.put("items", sbumap);
} else if (!type.getName().startsWith("java.") && !type.getName().startsWith("javax.")) {
String ct = simpleComponentType(logger, componentsMap, type, genericType);
if (ct == null) {
schemaMap.put("type", "object");
} else {
schemaMap.put("$ref", "#/components/schemas/" + ct);
}
} else {
schemaMap.put("type", "object");
}
}
private static String simpleComponentType(Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType) {
try {
Encodeable encodeable = JsonFactory.root().loadEncoder(genericType);
String ct = componentKey(logger, componentsMap, null, encodeable, true);
if (ct == null || ct.length() == 0) return null;
if (componentsMap.containsKey(ct)) return ct;
Map<String, Object> cmap = new LinkedHashMap<>();
componentsMap.put(ct, cmap); //必须在调用simpleSchemaType之前put不然嵌套情况下死循环
cmap.put("type", "object");
List<String> requireds = new ArrayList<>();
Map<String, Object> properties = new LinkedHashMap<>();
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);
String desc = "";
if (member.getField() != null) {
Column col = member.getField().getAnnotation(Column.class);
if (col == null) {
FilterColumn fcol = member.getField().getAnnotation(FilterColumn.class);
if (fcol != null) {
desc = fcol.comment();
if (fcol.required()) requireds.add(member.getAttribute().field());
}
} else {
desc = col.comment();
if (!col.nullable()) requireds.add(member.getAttribute().field());
}
if (desc.isEmpty() && member.getField().getAnnotation(Comment.class) != null) {
desc = member.getField().getAnnotation(Comment.class).value();
}
} else if (member.getMethod() != null) {
Column col = member.getMethod().getAnnotation(Column.class);
if (col == null) {
FilterColumn fcol = member.getMethod().getAnnotation(FilterColumn.class);
if (fcol != null) {
desc = fcol.comment();
if (fcol.required()) requireds.add(member.getAttribute().field());
}
} else {
desc = col.comment();
if (!col.nullable()) requireds.add(member.getAttribute().field());
}
if (desc.isEmpty() && member.getMethod().getAnnotation(Comment.class) != null) {
desc = member.getMethod().getAnnotation(Comment.class).value();
}
}
if (!desc.isEmpty()) schemaMap.put("description", desc);
properties.put(member.getAttribute().field(), schemaMap);
}
}
if (!requireds.isEmpty()) cmap.put("required", requireds);
cmap.put("properties", properties);
return ct;
} catch (Exception e) {
logger.log(Level.WARNING, genericType + " generate component info error", e);
return null;
}
}
private static String componentKey(Logger logger, Map<String, Map<String, Object>> componentsMap, EnMember field, Encodeable encodeable, boolean first) {
if (encodeable instanceof ObjectEncoder) {
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);
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 (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 (member.getEncoder() instanceof SimpledCoder) {
simpleSchemaType(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);
}
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);
if (subsb == null) return null;
if (sb.length() > 0 && subsb.length() > 0) sb.append("_");
sb.append(subsb);
} else if (member.getEncoder() instanceof MapEncoder) {
continue;
} else {
return null;
}
}
return sb.toString();
} else if (encodeable instanceof ArrayEncoder || encodeable instanceof CollectionEncoder) {
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);
if (sb == null || sb.isEmpty()) return sb;
if (field != null && field.getField() != null && field.getField().getDeclaringClass() == Sheet.class) {
return sb;
}
return sb + (array ? "_Array" : "_Collection");
} else if (encodeable instanceof SimpledCoder) {
Class stype = ((SimpledCoder) encodeable).getType();
if (stype.isPrimitive() || stype == Boolean.class || Number.class.isAssignableFrom(stype) || CharSequence.class.isAssignableFrom(stype)) {
return stype.getSimpleName();
}
return "";
} else if (encodeable instanceof MapEncoder) {
return first ? null : "";
} else {
return null;
}
}
private static Object formatExample(String example, Class type, Type genericType) {
if (example == null || example.isEmpty()) return null;
if (type == Flipper.class) {
return new Flipper();
} else if (TYPE_RETRESULT_OBJECT.equals(genericType)) {
return RetResult.success();
} else if (TYPE_RETRESULT_STRING.equals(genericType)) {
return RetResult.success();
} else if (TYPE_RETRESULT_INTEGER.equals(genericType)) {
return RetResult.success(0);
} else if (TYPE_RETRESULT_LONG.equals(genericType)) {
return RetResult.success(0L);
}
return example;
}
}

View File

@@ -43,6 +43,22 @@ public interface ApplicationListener {
default void postStart(Application application) { default void postStart(Application application) {
} }
/**
* Application 在运行Compile前调用
*
* @param application Application
*/
default void preCompile(Application application) {
}
/**
* Application 在运行Compile后调用
*
* @param application Application
*/
default void postCompile(Application application) {
}
/** /**
* Application 在运行shutdown前调用 * Application 在运行shutdown前调用
* *

View File

@@ -9,6 +9,7 @@ import java.io.*;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.net.*; import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -63,11 +64,11 @@ public final class ClassFilter<T> {
private final ClassLoader classLoader; private final ClassLoader classLoader;
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) { public ClassFilter(RedkaleClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
this(classLoader, annotationClass, superClass, excludeSuperClasses, null); this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
} }
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) { public ClassFilter(RedkaleClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
this.annotationClass = annotationClass; this.annotationClass = annotationClass;
this.superClass = superClass; this.superClass = superClass;
this.excludeSuperClasses = excludeSuperClasses; this.excludeSuperClasses = excludeSuperClasses;
@@ -75,8 +76,8 @@ public final class ClassFilter<T> {
this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
} }
public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) { public static ClassFilter create(RedkaleClassLoader classLoader, Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) {
ClassFilter filter = new ClassFilter(null, null, null, excludeSuperClasses); ClassFilter filter = new ClassFilter(classLoader, null, null, excludeSuperClasses);
filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";")); filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";"));
filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";")); filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";"));
filter.setPrivilegeIncludes(includeValues); filter.setPrivilegeIncludes(includeValues);
@@ -208,7 +209,7 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) { } catch (Throwable cfe) {
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF") && !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF")
&& !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("freemarker.")
&& !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) { && !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) {
//&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) { //&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe); logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe);
@@ -483,20 +484,20 @@ public final class ClassFilter<T> {
* 加载当前线程的classpath扫描所有class进行过滤 * 加载当前线程的classpath扫描所有class进行过滤
* *
* @param excludeFile 不需要扫描的文件夹 可以为null * @param excludeFile 不需要扫描的文件夹 可以为null
* @param loader RedkaleClassloader 不可为null
* @param excludeRegs 包含此关键字的文件将被跳过 可以为null * @param excludeRegs 包含此关键字的文件将被跳过 可以为null
* @param filters 过滤器 * @param filters 过滤器
* *
* @throws IOException 异常 * @throws IOException 异常
*/ */
public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException { public static void load(final File excludeFile, RedkaleClassLoader loader, final String[] excludeRegs, final ClassFilter... filters) throws IOException {
RedkaleClassLoader loader = (RedkaleClassLoader) Thread.currentThread().getContextClassLoader();
List<URL> urlfiles = new ArrayList<>(2); List<URL> urlfiles = new ArrayList<>(2);
List<URL> urljares = new ArrayList<>(2); List<URL> urljares = new ArrayList<>(2);
final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null; final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null;
final Pattern[] excludePatterns = toPattern(excludeRegs); final Pattern[] excludePatterns = toPattern(excludeRegs);
for (URL url : loader.getAllURLs()) { for (URL url : loader.getAllURLs()) {
if (exurl != null && exurl.sameFile(url)) continue; if (exurl != null && exurl.sameFile(url)) continue;
if (excludePatterns != null) { if (excludePatterns != null && url != RedkaleClassLoader.URL_NONE) {
boolean skip = false; boolean skip = false;
for (Pattern p : excludePatterns) { for (Pattern p : excludePatterns) {
if (p.matcher(url.toString()).matches()) { if (p.matcher(url.toString()).matches()) {
@@ -519,7 +520,7 @@ public final class ClassFilter<T> {
Set<String> classes = cache.get(url); Set<String> classes = cache.get(url);
if (classes == null) { if (classes == null) {
classes = new LinkedHashSet<>(); classes = new LinkedHashSet<>();
try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), "UTF-8"))) { try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8))) {
Enumeration<JarEntry> it = jar.entries(); Enumeration<JarEntry> it = jar.entries();
while (it.hasMoreElements()) { while (it.hasMoreElements()) {
String entryname = it.nextElement().getName().replace('/', '.'); String entryname = it.nextElement().getName().replace('/', '.');
@@ -527,6 +528,7 @@ public final class ClassFilter<T> {
String classname = entryname.substring(0, entryname.length() - 6); String classname = entryname.substring(0, entryname.length() - 6);
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
//常见的jar跳过 //常见的jar跳过
if (classname.startsWith("com.redkaledyn.")) break; //redkale动态生成的类
if (classname.startsWith("com.mysql.")) break; if (classname.startsWith("com.mysql.")) break;
if (classname.startsWith("org.mariadb.")) break; if (classname.startsWith("org.mariadb.")) break;
if (classname.startsWith("oracle.jdbc.")) break; if (classname.startsWith("oracle.jdbc.")) break;
@@ -553,17 +555,27 @@ public final class ClassFilter<T> {
Set<String> classes = cache.get(url); Set<String> classes = cache.get(url);
if (classes == null) { if (classes == null) {
classes = new LinkedHashSet<>(); classes = new LinkedHashSet<>();
files.clear(); final Set<String> cs = classes;
File root = new File(url.getFile()); if (url == RedkaleClassLoader.URL_NONE) loader.forEachCacheClass(v -> cs.add(v));
String rootpath = root.getPath(); if (cs.isEmpty()) {
loadClassFiles(excludeFile, root, files); files.clear();
for (File f : files) { File root = new File(url.getFile());
String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.'); String rootpath = root.getPath();
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; loadClassFiles(excludeFile, root, files);
classes.add(classname); for (File f : files) {
if (debug) debugstr.append(classname).append("\r\n"); String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.');
for (final ClassFilter filter : filters) { if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
if (filter != null) filter.filter(null, classname, url); classes.add(classname);
if (debug) debugstr.append(classname).append("\r\n");
for (final ClassFilter filter : filters) {
if (filter != null) filter.filter(null, classname, url);
}
}
} else {
for (String classname : classes) {
for (final ClassFilter filter : filters) {
if (filter != null) filter.filter(null, classname, url);
}
} }
} }
cache.put(url, classes); cache.put(url, classes);

View File

@@ -5,6 +5,8 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import java.io.*; import java.io.*;
import java.nio.file.*; import java.nio.file.*;
import static java.nio.file.StandardCopyOption.*; import static java.nio.file.StandardCopyOption.*;
@@ -24,12 +26,15 @@ import java.util.regex.Pattern;
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class LogFileHandler extends Handler { public class LoggingFileHandler extends Handler {
//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";
/** /**
* SNCP的日志输出Handler * SNCP的日志输出Handler
*/ */
public static class SncpLogFileHandler extends LogFileHandler { public static class LoggingSncpFileHandler extends LoggingFileHandler {
@Override @Override
public String getPrefix() { public String getPrefix() {
@@ -39,12 +44,11 @@ public class LogFileHandler extends Handler {
/** /**
* 默认的日志时间格式化类 * 默认的日志时间格式化类
* 与SimpleFormatter的区别在于level不使用本地化
* *
*/ */
public static class LoggingFormater extends Formatter { public static class LoggingFormater extends Formatter {
private static final String 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";
@Override @Override
public String format(LogRecord log) { public String format(LogRecord log) {
String source; String source;
@@ -71,7 +75,7 @@ public class LogFileHandler extends Handler {
pw.close(); pw.close();
throwable = sw.toString(); throwable = sw.toString();
} }
return String.format(format, return String.format(FORMATTER_FORMAT,
System.currentTimeMillis(), System.currentTimeMillis(),
source, source,
log.getLoggerName(), log.getLoggerName(),
@@ -79,7 +83,23 @@ public class LogFileHandler extends Handler {
message, message,
throwable); throwable);
} }
}
public static void initDebugLogConfig() {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(out);
ps.println("handlers = java.util.logging.ConsoleHandler");
ps.println(".level = FINEST");
ps.println("jdk.level = INFO");
ps.println("sun.level = INFO");
ps.println("com.sun.level = INFO");
ps.println("javax.level = INFO");
ps.println("java.util.logging.ConsoleHandler.level = FINEST");
ps.println("java.util.logging.ConsoleHandler.formatter = " + LoggingFileHandler.LoggingFormater.class.getName());
LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
} catch (Exception e) {
}
} }
protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue(); protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue();
@@ -114,7 +134,7 @@ public class LogFileHandler extends Handler {
private OutputStream logunusualstream; private OutputStream logunusualstream;
public LogFileHandler() { public LoggingFileHandler() {
updateTomorrow(); updateTomorrow();
configure(); configure();
open(); open();
@@ -218,7 +238,7 @@ public class LogFileHandler extends Handler {
private void configure() { private void configure() {
LogManager manager = LogManager.getLogManager(); LogManager manager = LogManager.getLogManager();
String cname = LogFileHandler.class.getName(); String cname = LoggingFileHandler.class.getName();
this.pattern = manager.getProperty(cname + ".pattern"); this.pattern = manager.getProperty(cname + ".pattern");
if (this.pattern == null) { if (this.pattern == null) {
this.pattern = "logs-%m/" + getPrefix() + "log-%d.log"; this.pattern = "logs-%m/" + getPrefix() + "log-%d.log";
@@ -280,6 +300,7 @@ public class LogFileHandler extends Handler {
try { try {
if (filterstr != null) { if (filterstr != null) {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr); Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
setFilter((Filter) clz.getDeclaredConstructor().newInstance()); setFilter((Filter) clz.getDeclaredConstructor().newInstance());
} }
} catch (Exception e) { } catch (Exception e) {
@@ -288,6 +309,7 @@ public class LogFileHandler extends Handler {
try { try {
if (formatterstr != null) { if (formatterstr != null) {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr); Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
setFormatter((Formatter) clz.getDeclaredConstructor().newInstance()); setFormatter((Formatter) clz.getDeclaredConstructor().newInstance());
} }
} catch (Exception e) { } catch (Exception e) {

View File

@@ -1,399 +1,446 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package org.redkale.boot; package org.redkale.boot;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.logging.Level; import java.util.logging.Level;
import javax.annotation.*; import java.util.stream.Stream;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR; import javax.annotation.*;
import org.redkale.boot.ClassFilter.FilterEntry; import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.cluster.ClusterAgent; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.mq.MessageAgent; import org.redkale.cluster.ClusterAgent;
import org.redkale.net.*; import org.redkale.mq.MessageAgent;
import org.redkale.net.http.*; import org.redkale.net.*;
import org.redkale.net.sncp.Sncp; import org.redkale.net.http.*;
import org.redkale.service.*; import org.redkale.net.sncp.Sncp;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.watch.*; import org.redkale.util.*;
import org.redkale.watch.*;
/**
* HTTP Server节点的配置Server /**
* * HTTP Server节点的配置Server
* <p> *
* 详情见: https://redkale.org * <p>
* * 详情见: https://redkale.org
* @author zhangjx *
*/ * @author zhangjx
@NodeProtocol("HTTP") */
public class NodeHttpServer extends NodeServer { @NodeProtocol("HTTP")
public class NodeHttpServer extends NodeServer {
protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
protected final HttpServer httpServer;
protected final HttpServer httpServer;
public NodeHttpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf)); public NodeHttpServer(Application application, AnyValue serconf) {
this.httpServer = (HttpServer) server; super(application, createServer(application, serconf));
this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null; this.httpServer = (HttpServer) server;
} this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null;
}
private static Server createServer(Application application, AnyValue serconf) {
return new HttpServer(application.getStartTime(), application.getResourceFactory().createChild()); private static Server createServer(Application application, AnyValue serconf) {
} return new HttpServer(application, application.getStartTime(), application.getResourceFactory().createChild());
}
public HttpServer getHttpServer() {
return httpServer; public HttpServer getHttpServer() {
} return httpServer;
}
@Override
public InetSocketAddress getSocketAddress() { @Override
return httpServer == null ? null : httpServer.getSocketAddress(); public InetSocketAddress getSocketAddress() {
} return httpServer == null ? null : httpServer.getSocketAddress();
}
@Override
@SuppressWarnings("unchecked") @Override
protected ClassFilter<Service> createServiceClassFilter() { @SuppressWarnings("unchecked")
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service"); protected ClassFilter<Service> createServiceClassFilter() {
} return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
}
@Override
@SuppressWarnings("unchecked") @Override
protected ClassFilter<Filter> createFilterClassFilter() { @SuppressWarnings("unchecked")
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter"); protected ClassFilter<Filter> createFilterClassFilter() {
} return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
}
@Override
@SuppressWarnings("unchecked") @Override
protected ClassFilter<Servlet> createServletClassFilter() { @SuppressWarnings("unchecked")
return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet"); protected ClassFilter<Servlet> createServletClassFilter() {
} return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
}
@Override
protected ClassFilter createOtherClassFilter() { @Override
return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket"); protected ClassFilter createOtherClassFilter() {
} return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket");
}
@Override
protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception { @Override
super.loadService(serviceFilter, otherFilter); protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
initWebSocketService(); super.loadService(serviceFilter, otherFilter);
} initWebSocketService();
}
@Override
protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception { @Override
if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter); protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
} if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
}
@Override
@SuppressWarnings("unchecked") @Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception { @SuppressWarnings("unchecked")
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter); protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
} if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
}
private void initWebSocketService() {
final NodeServer self = this; private void initWebSocketService() {
final ResourceFactory regFactory = application.getResourceFactory(); final NodeServer self = this;
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务 final ResourceFactory regFactory = application.getResourceFactory();
try { resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
if (field.getAnnotation(Resource.class) == null) return; try {
if (!(src instanceof WebSocketServlet)) return; if (field.getAnnotation(Resource.class) == null) return;
ResourceFactory.ResourceLoader loader = null; if (!(src instanceof WebSocketServlet)) return;
ResourceFactory sncpResFactory = null; ResourceFactory.ResourceLoader loader = null;
for (NodeServer ns : application.servers) { ResourceFactory sncpResFactory = null;
if (!ns.isSNCP()) continue; for (NodeServer ns : application.servers) {
sncpResFactory = ns.resourceFactory; if (!ns.isSNCP()) continue;
loader = sncpResFactory.findLoader(WebSocketNode.class, field); sncpResFactory = ns.resourceFactory;
if (loader != null) break; loader = sncpResFactory.findLoader(WebSocketNode.class, field);
} if (loader != null) break;
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment); }
synchronized (regFactory) { if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); synchronized (regFactory) {
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) { Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class)); if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class)); resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class));
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class)); resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class));
} resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
if (nodeService == null) { }
MessageAgent messageAgent = null; if (nodeService == null) {
try { MessageAgent messageAgent = null;
Field c = WebSocketServlet.class.getDeclaredField("messageAgent"); try {
c.setAccessible(true); Field c = WebSocketServlet.class.getDeclaredField("messageAgent");
messageAgent = (MessageAgent) c.get(src); c.setAccessible(true);
} catch (Exception ex) { messageAgent = (MessageAgent) c.get(src);
logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex); } catch (Exception ex) {
} logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null); }
regFactory.register(resourceName, WebSocketNode.class, nodeService); 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(nodeService, self);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService); field.set(src, nodeService);
} logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
} catch (Exception e) { }
logger.log(Level.SEVERE, "WebSocketNode inject error", e); } catch (Exception e) {
} logger.log(Level.SEVERE, "WebSocketNode inject error", e);
}, WebSocketNode.class); }
} }, WebSocketNode.class);
}
@SuppressWarnings("unchecked")
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception { @SuppressWarnings("unchecked")
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final String localThreadName = "[" + Thread.currentThread().getName() + "] "; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys()); final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
for (FilterEntry<? extends Filter> en : list) { List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType(); for (FilterEntry<? extends Filter> en : list) {
if (Modifier.isAbstract(clazz.getModifiers())) continue; Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance(); if (Modifier.isAbstract(clazz.getModifiers())) continue;
resourceFactory.inject(filter, this); RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
this.httpServer.addHttpFilter(filter, filterConf); resourceFactory.inject(filter, this);
if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
} this.httpServer.addHttpFilter(filter, filterConf);
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
} }
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
@SuppressWarnings("unchecked") }
protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets"); @SuppressWarnings("unchecked")
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", ""); RedkaleClassLoader.putReflectionPublicClasses(HttpServlet.class.getName());
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); RedkaleClassLoader.putReflectionPublicClasses(HttpPrepareServlet.class.getName());
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; RedkaleClassLoader.putReflectionDeclaredConstructors(HttpResourceServlet.class, HttpResourceServlet.class.getName());
final String prefix = prefix0; final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
final String localThreadName = "[" + Thread.currentThread().getName() + "] "; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys()); String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", "");
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType()); if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType()); final String prefix = prefix0;
if (ws1 == ws2) { final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
Priority p1 = o1.getType().getAnnotation(Priority.class); List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys());
Priority p2 = o2.getType().getAnnotation(Priority.class); list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0; boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType());
} if (ws1 == ws2) {
return ws1 ? -1 : 1; Priority p1 = o1.getType().getAnnotation(Priority.class);
}); Priority p2 = o2.getType().getAnnotation(Priority.class);
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>(); int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
for (FilterEntry<? extends Servlet> en : list) { return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0;
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType(); }
if (Modifier.isAbstract(clazz.getModifiers())) continue; return ws1 ? -1 : 1;
WebServlet ws = clazz.getAnnotation(WebServlet.class); });
if (ws == null) continue; final long starts = System.currentTimeMillis();
if (ws.value().length == 0) { final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName()); for (FilterEntry<? extends Servlet> en : list) {
continue; Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
} if (Modifier.isAbstract(clazz.getModifiers())) continue;
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance(); if (clazz.getAnnotation(Rest.RestDyn.class) != null) continue; //动态生成的跳过
resourceFactory.inject(servlet, this); WebServlet ws = clazz.getAnnotation(WebServlet.class);
final String[] mappings = ws.value(); if (ws == null) continue;
String pref = ws.repair() ? prefix : ""; if (ws.value().length == 0) {
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty(); logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName());
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings); continue;
if (ss != null) { }
for (int i = 0; i < mappings.length; i++) { RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
mappings[i] = pref + mappings[i]; final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
} resourceFactory.inject(servlet, this);
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings)); final String[] mappings = ws.value();
} String pref = ws.repair() ? prefix : "";
} DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
int max = 0; this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
if (ss != null && sb != null) { if (ss != null) {
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey())); for (int i = 0; i < mappings.length; i++) {
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { mappings[i] = pref + mappings[i];
if (as.getKey().length() > max) max = as.getKey().length(); }
} ss.add(new AbstractMap.SimpleEntry<>("HttpServlet (type=" + clazz.getName() + ")", mappings));
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { }
sb.append(localThreadName).append(" Load ").append(as.getKey()); }
for (int i = 0; i < max - as.getKey().length(); i++) { final List<AbstractMap.SimpleEntry<String, String[]>> rests = sb == null ? null : new ArrayList<>();
sb.append(' '); final List<AbstractMap.SimpleEntry<String, String[]>> webss = sb == null ? null : new ArrayList<>();
} if (rest && serverConf != null) {
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); final List<Object> restedObjects = new ArrayList<>();
} for (AnyValue restConf : serverConf.getAnyValues("rest")) {
} loadRestServlet(webSocketFilter, restConf, restedObjects, sb, rests, webss);
if (rest && serverConf != null) { }
final List<Object> restedObjects = new ArrayList<>(); }
for (AnyValue restConf : serverConf.getAnyValues("rest")) { int max = 0;
loadRestServlet(webSocketFilter, restConf, restedObjects, sb); if (ss != null && sb != null) {
} int maxTypeLength = 0;
} int maxNameLength = 0;
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim()); if (rests != null) {
} for (AbstractMap.SimpleEntry<String, String[]> en : rests) {
int pos = en.getKey().indexOf('#');
@SuppressWarnings("unchecked") if (pos > maxTypeLength) maxTypeLength = pos;
protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb) throws Exception { int len = en.getKey().length() - pos - 1;
if (!rest) return; if (len > maxNameLength) maxNameLength = len;
if (restConf == null) return; //不存在REST服务 }
}
final long starts = System.currentTimeMillis(); if (webss != null) {
String prefix0 = restConf.getValue("path", ""); for (AbstractMap.SimpleEntry<String, String[]> en : webss) {
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); int pos = en.getKey().indexOf('#');
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; if (pos > maxTypeLength) maxTypeLength = pos;
int len = en.getKey().length() - pos - 1;
final String localThreadName = "[" + Thread.currentThread().getName() + "] "; if (len > maxNameLength) maxNameLength = len;
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>(); }
String mqname = restConf.getValue("mq"); }
MessageAgent agent0 = null; if (rests != null) {
if (mqname != null) { for (AbstractMap.SimpleEntry<String, String[]> en : rests) {
agent0 = application.getMessageAgent(mqname); StringBuilder sub = new StringBuilder();
if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")"); int pos = en.getKey().indexOf('#');
} sub.append("RestDynServlet (type=").append(en.getKey().substring(0, pos));
final MessageAgent messageAgent = agent0; for (int i = 0; i < maxTypeLength - pos; i++) {
if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效 sub.append(' ');
final String prefix = prefix0; }
final boolean autoload = restConf.getBoolValue("autoload", true); sub.append(", name='").append(en.getKey().substring(pos + 1));
{ //加载RestService for (int i = 0; i < maxNameLength - pos; i++) {
String userTypeStr = restConf.getValue("usertype"); sub.append(' ');
final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr); }
sub.append("')");
final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName())); ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue()));
final Set<String> includeValues = new HashSet<>(); }
final Set<String> excludeValues = new HashSet<>(); }
for (AnyValue item : restConf.getAnyValues("service")) { if (webss != null) {
if (item.getBoolValue("ignore", false)) { for (AbstractMap.SimpleEntry<String, String[]> en : webss) {
excludeValues.add(item.getValue("value", "")); StringBuilder sub = new StringBuilder();
} else { int pos = en.getKey().indexOf('#');
includeValues.add(item.getValue("value", "")); sub.append("RestWebSocket (type=").append(en.getKey().substring(0, pos));
} for (int i = 0; i < maxTypeLength - pos; i++) {
} sub.append(' ');
}
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues); sub.append(", name='").append(en.getKey().substring(pos + 1));
final boolean finest = logger.isLoggable(Level.FINEST); for (int i = 0; i < maxNameLength - pos; i++) {
final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size()); sub.append(' ');
super.interceptorServices.stream().parallel().forEach((service) -> { }
try { sub.append("')");
final Class stype = Sncp.getServiceType(service); ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue()));
final String name = Sncp.getResourceName(service); }
RestService rs = (RestService) stype.getAnnotation(RestService.class); }
if (rs == null || rs.ignore()) return; ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
final String stypename = stype.getName(); if (as.getKey().length() > max) max = as.getKey().length();
if (!autoload && !includeValues.contains(stypename)) return; }
if (!restFilter.accept(stypename)) return; for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
synchronized (restedObjects) { sb.append(localThreadName).append("Load ").append(as.getKey());
if (restedObjects.contains(service)) { for (int i = 0; i < max - as.getKey().length(); i++) {
logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore"); sb.append(' ');
return; }
} sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
restedObjects.add(service); //避免重复创建Rest对象 }
} sb.append(localThreadName).append("All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR);
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix); }
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
String prefix2 = prefix; }
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = ""; @SuppressWarnings("unchecked")
resourceFactory.inject(servlet, NodeHttpServer.this); protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb,
dynServletMap.put(service, servlet); final List<AbstractMap.SimpleEntry<String, String[]>> rests, final List<AbstractMap.SimpleEntry<String, String[]>> webss) throws Exception {
if (messageAgent != null) messageAgent.putService(this, service, servlet); if (!rest) return;
//if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet); if (restConf == null) return; //不存在REST服务
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String prefix0 = restConf.getValue("path", "");
for (int i = 0; i < mappings.length; i++) { if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
mappings[i] = prefix2 + mappings[i]; if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
}
synchronized (ss) { final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName() + "(rest.name='" + name + "')", mappings)); String mqname = restConf.getValue("mq");
} MessageAgent agent0 = null;
} if (mqname != null) {
} finally { agent0 = application.getMessageAgent(mqname);
scdl.countDown(); if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")");
} }
}); final MessageAgent messageAgent = agent0;
scdl.await(); if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效
} final String prefix = prefix0;
if (webSocketFilter != null) { //加载RestWebSocket final boolean autoload = restConf.getBoolValue("autoload", true);
final Set<String> includeValues = new HashSet<>(); { //加载RestService
final Set<String> excludeValues = new HashSet<>(); String userTypeStr = restConf.getValue("usertype");
for (AnyValue item : restConf.getAnyValues("websocket")) { final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr);
if (item.getBoolValue("ignore", false)) {
excludeValues.add(item.getValue("value", "")); final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName()));
} else { final Set<String> includeValues = new HashSet<>();
includeValues.add(item.getValue("value", "")); final Set<String> excludeValues = new HashSet<>();
} for (AnyValue item : restConf.getAnyValues("service")) {
} if (item.getBoolValue("ignore", false)) {
excludeValues.add(item.getValue("value", ""));
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues); } else {
final boolean finest = logger.isLoggable(Level.FINEST); includeValues.add(item.getValue("value", ""));
}
List<FilterEntry<? extends WebSocket>> list = new ArrayList(webSocketFilter.getFilterEntrys()); }
for (FilterEntry<? extends WebSocket> en : list) {
Class<WebSocket> clazz = (Class<WebSocket>) en.getType(); final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues);
if (Modifier.isAbstract(clazz.getModifiers())) { final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size());
logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore"); Stream<Service> stream = super.interceptorServices.stream();
continue; if (!application.isCompileMode()) stream = stream.parallel(); //不能并行否则在maven plugin运行环境下ClassLoader不对
} stream.forEach((service) -> {
if (Modifier.isFinal(clazz.getModifiers())) { try {
logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore"); final Class stype = Sncp.getServiceType(service);
continue; final String name = Sncp.getResourceName(service);
} RestService rs = (RestService) stype.getAnnotation(RestService.class);
final Class<? extends WebSocket> stype = en.getType(); if (rs == null || rs.ignore()) return;
RestWebSocket rs = stype.getAnnotation(RestWebSocket.class);
if (rs == null || rs.ignore()) return; final String stypename = stype.getName();
if (!autoload && !includeValues.contains(stypename)) return;
final String stypename = stype.getName(); if (!restFilter.accept(stypename)) return;
if (!autoload && !includeValues.contains(stypename)) return; synchronized (restedObjects) {
if (!restFilter.accept(stypename)) return; if (restedObjects.contains(service)) {
if (restedObjects.contains(stype)) { logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore");
logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore"); return;
return; }
} restedObjects.add(service); //避免重复创建Rest对象
restedObjects.add(stype); //避免重复创建Rest对象 }
WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty()); HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
String prefix2 = prefix; String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = ""; if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet"); dynServletMap.put(service, servlet);
if (ss != null) { if (messageAgent != null) messageAgent.putService(this, service, servlet);
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); //if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
for (int i = 0; i < mappings.length; i++) { if (rests != null) {
mappings[i] = prefix2 + mappings[i]; String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
} for (int i = 0; i < mappings.length; i++) {
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings)); mappings[i] = prefix2 + mappings[i];
} }
} synchronized (rests) {
} rests.add(new AbstractMap.SimpleEntry<>(Sncp.getServiceType(service).getName() + "#" + name, mappings));
if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent); }
//输出信息 }
if (ss != null && !ss.isEmpty() && sb != null) { } finally {
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey())); scdl.countDown();
int max = 0; }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { });
if (as.getKey().length() > max) max = as.getKey().length(); scdl.await();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { if (webSocketFilter != null) { //加载RestWebSocket
sb.append(localThreadName).append(" Load ").append(as.getKey()); final Set<String> includeValues = new HashSet<>();
for (int i = 0; i < max - as.getKey().length(); i++) { final Set<String> excludeValues = new HashSet<>();
sb.append(' '); for (AnyValue item : restConf.getAnyValues("websocket")) {
} if (item.getBoolValue("ignore", false)) {
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); excludeValues.add(item.getValue("value", ""));
} } else {
sb.append(localThreadName).append(" All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR); includeValues.add(item.getValue("value", ""));
} }
} }
final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues);
@Override //loadServlet执行之后调用 final boolean finest = logger.isLoggable(Level.FINEST);
protected void postLoadServlets() {
final ClusterAgent cluster = application.clusterAgent; List<FilterEntry<? extends WebSocket>> list = new ArrayList(webSocketFilter.getFilterEntrys());
if (cluster != null) { for (FilterEntry<? extends WebSocket> en : list) {
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); Class<WebSocket> clazz = (Class<WebSocket>) en.getType();
String protocol = pros.value().toUpperCase(); if (Modifier.isAbstract(clazz.getModifiers())) {
if (!cluster.containsProtocol(protocol)) return; logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore");
if (!cluster.containsPort(server.getSocketAddress().getPort())) return; continue;
cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>()); }
} if (Modifier.isFinal(clazz.getModifiers())) {
} logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore");
continue;
@Override }
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) { final Class<? extends WebSocket> stype = en.getType();
cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>()); if (stype.getAnnotation(Rest.RestDyn.class) != null) continue;
} RestWebSocket rs = stype.getAnnotation(RestWebSocket.class);
} if (rs == null || rs.ignore()) continue;
final String stypename = stype.getName();
if (!autoload && !includeValues.contains(stypename)) continue;
if (!restFilter.accept(stypename)) continue;
if (restedObjects.contains(stype)) {
logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore");
continue;
}
restedObjects.add(stype); //避免重复创建Rest对象
WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty());
if (servlet == null) continue; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet");
if (webss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix2 + mappings[i];
}
synchronized (webss) {
webss.add(new AbstractMap.SimpleEntry<>(stype.getName() + "#" + rs.name(), mappings));
}
}
}
}
if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent);
}
@Override //loadServlet执行之后调用
protected void postLoadServlets() {
final ClusterAgent cluster = application.clusterAgent;
if (!application.isCompileMode() && cluster != null) {
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (!cluster.containsProtocol(protocol)) return;
if (!cluster.containsPort(server.getSocketAddress().getPort())) return;
cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>());
}
}
@Override
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) {
cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>());
}
}

View File

@@ -33,8 +33,8 @@ public class NodeSncpServer extends NodeServer {
private NodeSncpServer(Application application, AnyValue serconf) { private NodeSncpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf)); super(application, createServer(application, serconf));
this.sncpServer = (SncpServer) this.server; this.sncpServer = (SncpServer) this.server;
this.consumer = sncpServer == null || application.singletonrun ? null : (agent, x) -> {//singleton模式下不生成SncpServlet this.consumer = sncpServer == null || application.isSingletonMode() ? null : (agent, x) -> {//singleton模式下不生成SncpServlet
if (x.getClass().getAnnotation(Local.class) != null) return; if (x.getClass().getAnnotation(Local.class) != null) return; //本地模式的Service不生成SncpServlet
SncpDynServlet servlet = sncpServer.addSncpServlet(x); SncpDynServlet servlet = sncpServer.addSncpServlet(x);
dynServletMap.put(x, servlet); dynServletMap.put(x, servlet);
if (agent != null) agent.putService(this, x, servlet); if (agent != null) agent.putService(this, x, servlet);
@@ -46,7 +46,7 @@ public class NodeSncpServer extends NodeServer {
} }
private static Server createServer(Application application, AnyValue serconf) { private static Server createServer(Application application, AnyValue serconf) {
return new SncpServer(application.getStartTime(), application.getResourceFactory().createChild()); return new SncpServer(application, application.getStartTime(), serconf, application.getResourceFactory().createChild());
} }
@Override @Override
@@ -95,6 +95,7 @@ public class NodeSncpServer extends NodeServer {
for (FilterEntry<? extends Filter> en : list) { for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType(); Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue; if (Modifier.isAbstract(clazz.getModifiers())) continue;
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
final SncpFilter filter = clazz.getDeclaredConstructor().newInstance(); final SncpFilter filter = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(filter, this); resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
@@ -106,6 +107,8 @@ public class NodeSncpServer extends NodeServer {
@Override @Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception { protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
RedkaleClassLoader.putReflectionPublicClasses(SncpServlet.class.getName());
RedkaleClassLoader.putReflectionPublicClasses(SncpDynServlet.class.getName());
} }
@Override @Override

View File

@@ -0,0 +1,86 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.lang.reflect.Modifier;
import javax.persistence.Entity;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Decodeable;
import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.*;
import org.redkale.source.*;
import org.redkale.util.*;
/**
* 执行一次Application.run提前获取所有动态类
*
* @author zhangjx
* @since 2.5.0
*/
public class PrepareCompiler {
// public static void main(String[] args) throws Exception {
// new PrepareCompiler().run();
// }
public Application run() throws Exception {
final Application application = new Application(false, true, Application.loadAppConfig());
application.init();
for (ApplicationListener listener : application.listeners) {
listener.preStart(application);
}
for (ApplicationListener listener : application.listeners) {
listener.preCompile(application);
}
application.start();
final boolean hasSncp = application.getNodeServers().stream().filter(v -> v instanceof NodeSncpServer).findFirst().isPresent();
final String[] exlibs = (application.excludelibs != null ? (application.excludelibs + ";") : "").split(";");
final ClassFilter<?> entityFilter = new ClassFilter(application.getClassLoader(), Entity.class, Object.class, (Class[]) null);
final ClassFilter<?> beanFilter = new ClassFilter(application.getClassLoader(), Bean.class, Object.class, (Class[]) null);
final ClassFilter<?> filterFilter = new ClassFilter(application.getClassLoader(), null, FilterBean.class, (Class[]) null);
ClassFilter.Loader.load(application.getHome(), application.getClassLoader(), exlibs, entityFilter, beanFilter, filterFilter);
for (FilterEntry en : entityFilter.getFilterEntrys()) {
Class clz = en.getType();
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue;
try {
application.dataSources.forEach(source -> source.compile(clz));
JsonFactory.root().loadEncoder(clz);
if (hasSncp) BsonFactory.root().loadEncoder(clz);
Decodeable decoder = JsonFactory.root().loadDecoder(clz);
if (hasSncp) BsonFactory.root().loadDecoder(clz);
decoder.convertFrom(new JsonReader("{}"));
} catch (Exception e) { //JsonFactory.loadDecoder可能会失败因为class可能包含抽象类字段,如ColumnValue.value字段
}
}
for (FilterEntry en : beanFilter.getFilterEntrys()) {
Class clz = en.getType();
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue;
try {
JsonFactory.root().loadEncoder(clz);
if (hasSncp) BsonFactory.root().loadEncoder(clz);
Decodeable decoder = JsonFactory.root().loadDecoder(clz);
if (hasSncp) BsonFactory.root().loadDecoder(clz);
decoder.convertFrom(new JsonReader("{}"));
} catch (Exception e) { //JsonFactory.loadDecoder可能会失败因为class可能包含抽象类字段,如ColumnValue.value字段
}
}
for (FilterEntry en : filterFilter.getFilterEntrys()) {
Class clz = en.getType();
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue;
try {
FilterNodeBean.load(clz);
} catch (Exception e) {
}
}
for (ApplicationListener listener : application.listeners) {
listener.postCompile(application);
}
application.shutdown();
return application;
}
}

View File

@@ -55,8 +55,8 @@
} else { } else {
var w = param.required ? "font-weight:bold;" : ""; var w = param.required ? "font-weight:bold;" : "";
var c = ' style="' + w + '"'; var c = ' style="' + w + '"';
if (param.src == "HEADER") c = ' style="color:red;' + w + '"'; if (param.style == "HEADER") c = ' style="color:red;' + w + '"';
if (param.src == "COOKIE") c = ' style="color:blue;' + w + '"'; if (param.style == "COOKIE") c = ' style="color:blue;' + w + '"';
paramshtml.push('<tr><td ' + c + '> ' + param.name + ' </td><td> ' + t + '</td><td> ' + param.comment + '</td></tr>'); paramshtml.push('<tr><td ' + c + '> ' + param.name + ' </td><td> ' + t + '</td><td> ' + param.comment + '</td></tr>');
} }
} }

View File

@@ -15,9 +15,15 @@ import org.redkale.watch.WatchService;
*/ */
public abstract class AbstractWatchService extends AbstractService implements WatchService { public abstract class AbstractWatchService extends AbstractService implements WatchService {
/**
* 缺少参数
*/
@Comment("缺少参数") @Comment("缺少参数")
public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001; public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001;
/**
* 执行异常
*/
@Comment("执行异常") @Comment("执行异常")
public static final int RET_WATCH_RUN_EXCEPTION = 1600_0002; public static final int RET_WATCH_RUN_EXCEPTION = 1600_0002;
} }

View File

@@ -61,7 +61,7 @@ public class ServerWatchService extends AbstractWatchService {
final Server server = node.getServer(); final Server server = node.getServer();
InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport); InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport);
try { try {
server.changeAddress(newAddr); server.changeAddress(application, newAddr);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error"); return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error");
@@ -72,7 +72,7 @@ public class ServerWatchService extends AbstractWatchService {
private Map<String, Object> formatToMap(NodeServer node) { private Map<String, Object> formatToMap(NodeServer node) {
Server server = node.getServer(); Server server = node.getServer();
Map<String, Object> rs = new LinkedHashMap<>(); Map<String, Object> rs = new LinkedHashMap<>();
String protocol = server.getProtocol(); String protocol = server.getNetprotocol();
if (node instanceof NodeSncpServer) { if (node instanceof NodeSncpServer) {
protocol += "/SNCP"; protocol += "/SNCP";
} else if (node instanceof NodeWatchServer) { } else if (node instanceof NodeWatchServer) {
@@ -86,7 +86,6 @@ public class ServerWatchService extends AbstractWatchService {
rs.put("name", server.getName()); rs.put("name", server.getName());
rs.put("protocol", protocol); rs.put("protocol", protocol);
rs.put("address", server.getSocketAddress()); rs.put("address", server.getSocketAddress());
rs.put("threads", server.getThreads());
rs.put("backlog", server.getBacklog()); rs.put("backlog", server.getBacklog());
rs.put("bufferCapacity", server.getBufferCapacity()); rs.put("bufferCapacity", server.getBufferCapacity());
rs.put("bufferPoolSize", server.getBufferPoolSize()); rs.put("bufferPoolSize", server.getBufferPoolSize());

View File

@@ -81,7 +81,7 @@ public class TransportWatchService extends AbstractWatchService {
break; break;
} }
} }
application.restoreConfig(); //application.restoreConfig();
} }
return RetResult.success(); return RetResult.success();
} }
@@ -113,7 +113,7 @@ public class TransportWatchService extends AbstractWatchService {
break; break;
} }
} }
application.restoreConfig(); //application.restoreConfig();
} }
return RetResult.success(); return RetResult.success();
} }

View File

@@ -0,0 +1,327 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.cluster;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import javax.annotation.Resource;
import org.redkale.boot.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.service.Service;
import org.redkale.source.CacheSource;
import org.redkale.util.*;
/**
* 使用CacheSource实现的第三方服务发现管理接口cluster
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public class CacheClusterAgent extends ClusterAgent implements Resourcable {
@Resource(name = "$")
private CacheSource source;
private String sourceName;
protected int ttls = 10; //定时检查的秒数
protected ScheduledThreadPoolExecutor scheduler;
//可能被HttpMessageClient用到的服务 key: servicename
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> httpAddressMap = new ConcurrentHashMap<>();
//可能被mqtp用到的服务 key: servicename
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> mqtpAddressMap = new ConcurrentHashMap<>();
@Override
public void init(AnyValue config) {
super.init(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;
}
}
}
@Override
public void destroy(AnyValue config) {
if (scheduler != null) scheduler.shutdownNow();
}
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;
}
@Override
public String resourceName() {
return sourceName;
}
@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;
}
@Override
public void start() {
if (this.scheduler == null) {
this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> {
final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread");
t.setDaemon(true);
return t;
});
this.scheduler.scheduleAtFixedRate(() -> {
try {
checkApplicationHealth();
checkHttpAddressHealth();
loadMqtpAddressHealth();
localEntrys.values().stream().filter(e -> !e.canceled).forEach(entry -> {
checkLocalHealth(entry);
});
remoteEntrys.values().stream().filter(entry -> "SNCP".equalsIgnoreCase(entry.protocol)).forEach(entry -> {
updateSncpTransport(entry);
});
} catch (Exception e) {
logger.log(Level.SEVERE, "scheduleAtFixedRate check error", e instanceof CompletionException ? ((CompletionException) e).getCause() : e);
}
}, Math.max(2000, ttls * 1000), Math.max(2000, ttls * 1000), TimeUnit.MILLISECONDS);
}
}
protected void loadMqtpAddressHealth() {
List<String> keys = source.queryKeysStartsWith("cluster.mqtp:");
keys.forEach(servicename -> {
try {
this.mqtpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS));
} catch (Exception e) {
logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + servicename + " error", e);
}
});
}
protected void checkHttpAddressHealth() {
try {
this.httpAddressMap.keySet().stream().forEach(servicename -> {
try {
this.httpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS));
} catch (Exception e) {
logger.log(Level.SEVERE, "checkHttpAddressHealth check " + servicename + " error", e);
}
});
} catch (Exception ex) {
logger.log(Level.SEVERE, "checkHttpAddressHealth check error", ex);
}
}
protected void checkLocalHealth(final ClusterEntry entry) {
AddressEntry newaddr = new AddressEntry();
newaddr.addr = entry.address;
newaddr.nodeid = this.nodeid;
newaddr.time = System.currentTimeMillis();
source.hset(entry.checkname, entry.checkid, AddressEntry.class, newaddr);
}
@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) + ":";
mqtpAddressMap.keySet().stream().filter(k -> k.startsWith(servicenamprefix))
.forEach(sn -> rsmap.put(sn.substring(servicenamprefix.length()), mqtpAddressMap.get(sn)));
return CompletableFuture.completedFuture(rsmap);
}
@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);
if (rs != null) return CompletableFuture.completedFuture(rs);
return queryAddress(servicename).thenApply(t -> {
httpAddressMap.put(servicename, t);
return t;
});
}
@Override
protected CompletableFuture<Collection<InetSocketAddress>> queryAddress(final ClusterEntry entry) {
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);
return future.thenApply(map -> {
final Set<InetSocketAddress> set = new HashSet<>();
map.forEach((n, v) -> {
if (v != null && (System.currentTimeMillis() - v.time) / 1000 < ttls) set.add(v.addr);
});
return set;
});
}
protected boolean isApplicationHealth() {
String servicename = generateApplicationServiceName();
String serviceid = generateApplicationServiceId();
AddressEntry entry = (AddressEntry) source.hget(servicename, serviceid, AddressEntry.class);
return entry != null && (System.currentTimeMillis() - entry.time) / 1000 < ttls;
}
protected void checkApplicationHealth() {
String checkname = generateApplicationServiceName();
String checkid = generateApplicationCheckId();
AddressEntry entry = new AddressEntry();
entry.addr = this.appAddress;
entry.nodeid = this.nodeid;
entry.time = System.currentTimeMillis();
source.hset(checkname, checkid, AddressEntry.class, entry);
}
@Override
public void register(Application application) {
if (isApplicationHealth()) throw new RuntimeException("application.nodeid=" + nodeid + " exists in cluster");
deregister(application);
String serviceid = generateApplicationServiceId();
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);
}
@Override
public void deregister(Application application) {
String servicename = generateApplicationServiceName();
source.remove(servicename);
}
@Override
protected ClusterEntry register(NodeServer ns, String protocol, Service service) {
deregister(ns, protocol, service, false);
//
ClusterEntry clusterEntry = new ClusterEntry(ns, protocol, service);
AddressEntry entry = new AddressEntry();
entry.addr = clusterEntry.address;
entry.nodeid = this.nodeid;
entry.time = System.currentTimeMillis();
source.hset(clusterEntry.servicename, clusterEntry.serviceid, AddressEntry.class, entry);
return clusterEntry;
}
@Override
protected void deregister(NodeServer ns, String protocol, Service service) {
deregister(ns, protocol, service, true);
}
protected void deregister(NodeServer ns, String protocol, Service service, boolean realcanceled) {
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)) {
currEntry = entry;
break;
}
}
if (currEntry == null) {
for (final ClusterEntry entry : remoteEntrys.values()) {
if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) {
currEntry = entry;
break;
}
}
}
source.hremove(servicename, serviceid);
if (realcanceled && currEntry != null) currEntry.canceled = true;
if (!"mqtp".equals(protocol) && currEntry != null && currEntry.submqtp) {
deregister(ns, "mqtp", service, realcanceled);
}
}
@Override
protected String generateApplicationServiceName() {
return "cluster." + super.generateApplicationServiceName();
}
@Override
protected String generateServiceName(NodeServer ns, String protocol, Service service) {
return "cluster." + super.generateServiceName(ns, protocol, service);
}
@Override
public String generateHttpServiceName(String protocol, String module, String resname) {
return "cluster." + super.generateHttpServiceName(protocol, module, resname);
}
@Override
protected String generateApplicationCheckName() {
return generateApplicationServiceName();
}
@Override
protected String generateApplicationCheckId() {
return generateApplicationServiceId();
}
@Override
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
return generateServiceName(ns, protocol, service);
}
@Override
protected String generateCheckId(NodeServer ns, String protocol, Service service) {
return generateServiceId(ns, protocol, service);
}
public static class AddressEntry {
public InetSocketAddress addr;
public int nodeid;
public long time;
public AddressEntry() {
}
public AddressEntry refresh() {
this.time = System.currentTimeMillis();
return this;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
}

View File

@@ -85,7 +85,7 @@ public abstract class ClusterAgent {
} }
//ServiceLoader时判断配置是否符合当前实现类 //ServiceLoader时判断配置是否符合当前实现类
public abstract boolean match(AnyValue config); public abstract boolean acceptsConf(AnyValue config);
public boolean containsProtocol(String protocol) { public boolean containsProtocol(String protocol) {
if (protocol == null || protocol.isEmpty()) return false; if (protocol == null || protocol.isEmpty()) return false;
@@ -107,15 +107,14 @@ public abstract class ClusterAgent {
//注册本地模式 //注册本地模式
for (Service service : localServices) { for (Service service : localServices) {
if (!canRegister(protocol, service)) continue; if (!canRegister(protocol, service)) continue;
register(ns, protocol, service); ClusterEntry htentry = register(ns, protocol, service);
ClusterEntry htentry = new ClusterEntry(ns, protocol, service);
localEntrys.put(htentry.serviceid, htentry); localEntrys.put(htentry.serviceid, htentry);
if (protocol.toLowerCase().startsWith("http")) { if (protocol.toLowerCase().startsWith("http")) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) { if (mmc != null) {
register(ns, "mqtp", service); ClusterEntry mqentry = register(ns, "mqtp", service);
ClusterEntry mqentry = new ClusterEntry(ns, "mqtp", service);
localEntrys.put(mqentry.serviceid, mqentry); localEntrys.put(mqentry.serviceid, mqentry);
htentry.submqtp = true;
} }
} }
} }
@@ -141,6 +140,8 @@ public abstract class ClusterAgent {
protected boolean canRegister(String protocol, Service service) { protected boolean canRegister(String protocol, Service service) {
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false; 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 (service instanceof WebSocketNode) {
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false; if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
} }
@@ -176,7 +177,7 @@ public abstract class ClusterAgent {
protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry); protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry);
//注册服务 //注册服务
protected abstract void register(NodeServer ns, String protocol, Service service); protected abstract ClusterEntry register(NodeServer ns, String protocol, Service service);
//注销服务 //注销服务
protected abstract void deregister(NodeServer ns, String protocol, Service service); protected abstract void deregister(NodeServer ns, String protocol, Service service);
@@ -313,16 +314,24 @@ public abstract class ClusterAgent {
public boolean canceled; public boolean canceled;
public boolean submqtp;
public ClusterEntry(NodeServer ns, String protocol, Service service) { public ClusterEntry(NodeServer ns, String protocol, Service service) {
this.serviceid = generateServiceId(ns, protocol, 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.checkid = generateCheckId(ns, protocol, service);
this.checkname = generateCheckName(ns, protocol, service); this.checkname = generateCheckName(ns, protocol, service);
this.protocol = protocol; this.protocol = protocol;
this.address = ns.getSocketAddress(); InetSocketAddress addr = ns.getSocketAddress();
String host = addr.getHostString();
if ("0.0.0.0".equals(host)) {
host = appAddress.getHostString();
addr = new InetSocketAddress(host, addr.getPort());
}
this.address = addr;
this.serviceref = new WeakReference(service); this.serviceref = new WeakReference(service);
Server server = ns.getServer(); Server server = ns.getServer();
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_PROTOCOL; this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL;
} }
@Override @Override

View File

@@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.cluster;
import org.redkale.util.AnyValue;
/**
* 自定义的ClusterAgent加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.5.0
*/
public interface ClusterAgentProvider {
public boolean acceptsConf(AnyValue config);
public Class<? extends ClusterAgent> agentClass();
}

View File

@@ -14,7 +14,7 @@ import static org.redkale.convert.Reader.ValueType.MAP;
/** /**
* 对不明类型的对象进行反序列化 <br> * 对不明类型的对象进行反序列化 <br>
* <b>注意: 目前只支持文本格式</b> <br> * <b>注意: 目前只支持文本格式</b> <br>
* <p> *
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
@@ -31,12 +31,17 @@ public class AnyDecoder implements Decodeable<Reader, Object> {
private static final Creator<HashMap> mapCreator = Creator.create(HashMap.class); private static final Creator<HashMap> mapCreator = Creator.create(HashMap.class);
protected final Decodeable<Reader, String> stringDecoder; final Decodeable<Reader, String> stringDecoder;
protected final CollectionDecoder collectionDecoder; final CollectionDecoder collectionDecoder;
protected final MapDecoder mapDecoder; final MapDecoder mapDecoder;
/**
* 构造函数
*
* @param factory ConvertFactory
*/
public AnyDecoder(final ConvertFactory factory) { public AnyDecoder(final ConvertFactory factory) {
this.stringDecoder = factory.loadDecoder(String.class); this.stringDecoder = factory.loadDecoder(String.class);
this.collectionDecoder = new CollectionDecoder(factory, collectionObjectType, Object.class, collectionCreator, this); this.collectionDecoder = new CollectionDecoder(factory, collectionObjectType, Object.class, collectionCreator, this);

View File

@@ -0,0 +1,49 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.Type;
/**
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入WriterJSON则不写入。
*
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 序列化的泛型类型
*/
public final class AnyEncoder<T> implements Encodeable<Writer, T> {
final ConvertFactory factory;
AnyEncoder(ConvertFactory factory) {
this.factory = factory;
}
@Override
@SuppressWarnings("unchecked")
public void convertTo(final Writer out, final T value) {
if (value == null) {
out.writeClassName(null);
out.writeNull();
} else {
Class clazz = value.getClass();
if (clazz == Object.class) {
out.writeObjectB(value);
out.writeObjectE(value);
return;
}
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clazz));
factory.loadEncoder(clazz).convertTo(out, value);
}
}
@Override
public Type getType() {
return Object.class;
}
}

View File

@@ -0,0 +1,40 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.Type;
import org.redkale.util.AnyValue;
/**
* AnyValue的Decoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
*
* @since 2.5.0
*/
public class AnyValueDecoder<R extends Reader> implements Decodeable<R, AnyValue> {
protected final ConvertFactory factory;
public AnyValueDecoder(final ConvertFactory factory) {
this.factory = factory;
}
@Override
public AnyValue convertFrom(R in) {
return null;
}
@Override
public Type getType() {
return AnyValue.class;
}
}

View File

@@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.Type;
import org.redkale.util.AnyValue;
/**
* AnyValue的Encoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <W> Writer输出的子类
*
* @since 2.5.0
*/
public class AnyValueEncoder<W extends Writer> implements Encodeable<W, AnyValue> {
@Override
public void convertTo(W out, AnyValue value) {
}
@Override
public Type getType() {
return AnyValue.class;
}
}

View File

@@ -29,6 +29,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
protected final Encodeable<Writer, Object> componentEncoder; protected final Encodeable<Writer, Object> componentEncoder;
protected final boolean subtypefinal;
protected volatile boolean inited = false; protected volatile boolean inited = false;
protected final Object lock = new Object(); protected final Object lock = new Object();
@@ -47,6 +49,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
factory.register(type, this); factory.register(type, this);
this.componentEncoder = factory.loadEncoder(this.componentType); this.componentEncoder = factory.loadEncoder(this.componentType);
this.anyEncoder = factory.getAnyEncoder(); this.anyEncoder = factory.getAnyEncoder();
this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
} finally { } finally {
inited = true; inited = true;
synchronized (lock) { synchronized (lock) {
@@ -65,8 +68,9 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
out.writeNull(); out.writeNull();
return; return;
} }
if (value.length == 0) { int iMax = value.length - 1;
out.writeArrayB(0, this, componentEncoder, value); if (iMax == -1) {
out.writeArrayB(0, this, componentEncoder, value);
out.writeArrayE(); out.writeArrayE();
return; return;
} }
@@ -81,19 +85,30 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
} }
} }
} }
if (out.writeArrayB(value.length, this, componentEncoder, value) < 0) { Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
final Type comp = this.componentType; if (subtypefinal) {
boolean first = true; if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
for (Object v : value) { for (int i = 0;; i++) {
if (!first) out.writeArrayMark(); writeMemberValue(out, member, itemEncoder, value[i], i);
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? componentEncoder : anyEncoder), v, first); if (i == iMax) break;
if (first) first = false; out.writeArrayMark();
}
}
} else {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
final Type comp = this.componentType;
for (int i = 0;; i++) {
Object v = value[i];
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, i);
if (i == iMax) break;
out.writeArrayMark();
}
} }
} }
out.writeArrayE(); out.writeArrayE();
} }
protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, boolean first) { protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, int index) {
encoder.convertTo(out, value); encoder.convertTo(out, value);
} }

View File

@@ -32,5 +32,4 @@ public abstract class BinaryConvert<R extends Reader, W extends Writer> extends
public abstract byte[] convertTo(final Type type, final Object value); public abstract byte[] convertTo(final Type type, final Object value);
public abstract byte[] convertMapTo(final Object... values);
} }

View File

@@ -8,7 +8,7 @@ package org.redkale.convert;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.*; import java.util.function.*;
import org.redkale.util.Attribute; import org.redkale.util.*;
/** /**
* 序列化/反序列化操作类 * 序列化/反序列化操作类
@@ -57,14 +57,24 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers); public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers);
public abstract void convertTo(final W writer, final Object value);
public abstract void convertTo(final W writer, final Type type, final Object value);
public abstract byte[] convertToBytes(final Object value); public abstract byte[] convertToBytes(final Object value);
public abstract byte[] convertToBytes(final Type type, final Object value); public abstract byte[] convertToBytes(final Type type, final Object value);
public abstract void convertToBytes(final Object value, final ConvertBytesHandler handler);
public abstract void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler);
public abstract void convertToBytes(final ByteArray array, final Object value);
public abstract void convertToBytes(final ByteArray array, final Type type, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value); public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value); public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);
public abstract ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values);
} }

View File

@@ -0,0 +1,23 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.util.function.Consumer;
/**
*
* convertToBytes系列的方法的回调
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public interface ConvertBytesHandler {
<A> void completed(byte[] bs, int offset, int length, Consumer<A> callback, A attachment);
}

View File

@@ -42,7 +42,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
protected Convert<R, W> convert; protected Convert<R, W> convert;
protected boolean tiny; protected boolean tiny; //String类型值为""Boolean类型值为false时是否需要输出 默认为true
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this); private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this);
@@ -71,6 +71,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.parent = parent; this.parent = parent;
if (parent == null) { if (parent == null) {
//--------------------------------------------------------- //---------------------------------------------------------
this.register(boolean.class, BoolSimpledCoder.instance); this.register(boolean.class, BoolSimpledCoder.instance);
this.register(Boolean.class, BoolSimpledCoder.instance); this.register(Boolean.class, BoolSimpledCoder.instance);
@@ -101,17 +102,23 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(CharSequence.class, CharSequenceSimpledCoder.instance); this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
this.register(StringBuilder.class, CharSequenceSimpledCoder.StringBuilderSimpledCoder.instance); this.register(StringBuilder.class, CharSequenceSimpledCoder.StringBuilderSimpledCoder.instance);
this.register(java.util.Date.class, DateSimpledCoder.instance); this.register(java.util.Date.class, DateSimpledCoder.instance);
this.register(java.time.Instant.class, InstantSimpledCoder.instance);
this.register(java.time.LocalDate.class, LocalDateSimpledCoder.instance);
this.register(java.time.LocalTime.class, LocalTimeSimpledCoder.instance);
this.register(java.time.LocalDateTime.class, LocalDateTimeSimpledCoder.instance);
this.register(java.time.Duration.class, DurationSimpledCoder.instance); this.register(java.time.Duration.class, DurationSimpledCoder.instance);
this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance); this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance);
this.register(AtomicLong.class, AtomicLongSimpledCoder.instance); this.register(AtomicLong.class, AtomicLongSimpledCoder.instance);
this.register(BigInteger.class, BigIntegerSimpledCoder.instance); this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
this.register(BigDecimal.class, BigDecimalSimpledCoder.instance); this.register(BigDecimal.class, BigDecimalSimpledCoder.instance);
this.register(InetAddress.class, InetAddressSimpledCoder.instance); this.register(InetAddress.class, InetAddressSimpledCoder.instance);
this.register(LongAdder.class, LongAdderSimpledCoder.instance);
this.register(DLong.class, DLongSimpledCoder.instance); this.register(DLong.class, DLongSimpledCoder.instance);
this.register(Class.class, TypeSimpledCoder.instance); this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance); this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance); this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance); this.register(File.class, FileSimpledCoder.instance);
this.register(Throwable.class, ThrowableSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance); this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance); this.register(URL.class, URLSimpledCoder.instance);
this.register(URI.class, URISimpledCoder.instance); this.register(URI.class, URISimpledCoder.instance);
@@ -140,48 +147,51 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
}); });
try { try {
Class sqldateClass = Class.forName("java.sql.Date"); Class sqldateClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Date");
this.register(sqldateClass, new SimpledCoder<R, W, java.sql.Date>() { Invoker<Object, Object> sqldateInvoker = Invoker.create(sqldateClass, "valueOf", String.class);
this.register(sqldateClass, new SimpledCoder<R, W, Object>() {
@Override @Override
public void convertTo(W out, java.sql.Date value) { public void convertTo(W out, Object value) {
out.writeSmallString(value == null ? null : value.toString()); out.writeSmallString(value == null ? null : value.toString());
} }
@Override @Override
public java.sql.Date convertFrom(R in) { public Object convertFrom(R in) {
String t = in.readSmallString(); String t = in.readSmallString();
return t == null ? null : java.sql.Date.valueOf(t); return t == null ? null : sqldateInvoker.invoke(null, t);
} }
}); });
Class sqltimeClass = Class.forName("java.sql.Time"); Class sqltimeClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Time");
this.register(sqltimeClass, new SimpledCoder<R, W, java.sql.Time>() { Invoker<Object, Object> sqltimeInvoker = Invoker.create(sqltimeClass, "valueOf", String.class);
this.register(sqltimeClass, new SimpledCoder<R, W, Object>() {
@Override @Override
public void convertTo(W out, java.sql.Time value) { public void convertTo(W out, Object value) {
out.writeSmallString(value == null ? null : value.toString()); out.writeSmallString(value == null ? null : value.toString());
} }
@Override @Override
public java.sql.Time convertFrom(R in) { public Object convertFrom(R in) {
String t = in.readSmallString(); String t = in.readSmallString();
return t == null ? null : java.sql.Time.valueOf(t); return t == null ? null : sqltimeInvoker.invoke(null, t);
} }
}); });
Class timestampClass = Class.forName("java.sql.Timestamp"); Class timestampClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Timestamp");
this.register(timestampClass, new SimpledCoder<R, W, java.sql.Timestamp>() { Invoker<Object, Object> timestampInvoker = Invoker.create(timestampClass, "valueOf", String.class);
this.register(timestampClass, new SimpledCoder<R, W, Object>() {
@Override @Override
public void convertTo(W out, java.sql.Timestamp value) { public void convertTo(W out, Object value) {
out.writeSmallString(value == null ? null : value.toString()); out.writeSmallString(value == null ? null : value.toString());
} }
@Override @Override
public java.sql.Timestamp convertFrom(R in) { public Object convertFrom(R in) {
String t = in.readSmallString(); String t = in.readSmallString();
return t == null ? null : java.sql.Timestamp.valueOf(t); return t == null ? null : timestampInvoker.invoke(null, t);
} }
}); });
@@ -203,9 +213,11 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
} }
synchronized (loaderInited) { synchronized (loaderInited) {
if (!loaderInited.get()) { if (!loaderInited.get()) {
Iterator<ConvertLoader> it = ServiceLoader.load(ConvertLoader.class).iterator(); Iterator<ConvertProvider> it = ServiceLoader.load(ConvertProvider.class).iterator();
RedkaleClassLoader.putServiceLoader(ConvertProvider.class);
while (it.hasNext()) { while (it.hasNext()) {
ConvertLoader cl = it.next(); ConvertProvider cl = it.next();
RedkaleClassLoader.putReflectionPublicConstructors(cl.getClass(), cl.getClass().getName());
if (cl.type() == ConvertType.PROTOBUF) defProtobufConvert = cl.convert(); if (cl.type() == ConvertType.PROTOBUF) defProtobufConvert = cl.convert();
} }
loaderInited.set(true); loaderInited.set(true);
@@ -232,11 +244,33 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return new EnumSimpledCoder(enumClass); return new EnumSimpledCoder(enumClass);
} }
protected Type formatObjectType(Type type) {
if (type instanceof Class) {
Class<?> clazz = (Class) type;
ConvertImpl ci = clazz.getAnnotation(ConvertImpl.class);
if (ci != null) {
if (!Modifier.isAbstract(clazz.getModifiers()) && !Modifier.isInterface(clazz.getModifiers())) {
throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + " must at interface or abstract class, but " + clazz + " not");
}
Class impl = ci.value();
if (Modifier.isAbstract(impl.getModifiers()) || Modifier.isInterface(impl.getModifiers())) {
throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + " at class " + impl + " cannot be interface or abstract class");
}
return impl;
}
}
return type;
}
protected <E> Encodeable<W, E> createDyncEncoder(Type type) {
return null;
}
protected ObjectDecoder createObjectDecoder(Type type) { protected ObjectDecoder createObjectDecoder(Type type) {
return new ObjectDecoder(type); return new ObjectDecoder(type);
} }
protected ObjectEncoder createObjectEncoder(Type type) { protected <E> ObjectEncoder<W, E> createObjectEncoder(Type type) {
return new ObjectEncoder(type); return new ObjectEncoder(type);
} }
@@ -354,10 +388,20 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return null; return null;
} }
static Field readGetSetField(Method method) {
String name = readGetSetFieldName(method);
if (name == null) return null;
try {
return method.getDeclaringClass().getDeclaredField(name);
} catch (Exception e) {
return null;
}
}
static String readGetSetFieldName(Method method) { static String readGetSetFieldName(Method method) {
if (method == null) return null; if (method == null) return null;
String fname = method.getName(); String fname = method.getName();
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; //record类会直接用field名作为method名
fname = fname.substring(fname.startsWith("is") ? 2 : 3); fname = fname.substring(fname.startsWith("is") ? 2 : 3);
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) { if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1); fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
@@ -721,12 +765,15 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
try { try {
method.setAccessible(true); method.setAccessible(true);
simpleCoder = (Decodeable) method.invoke(null, this); simpleCoder = (Decodeable) method.invoke(null, this);
RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName());
RedkaleClassLoader.putReflectionMethod(clazz.getName(), method);
break; break;
} catch (Exception e) { } catch (Exception e) {
} }
} }
if (simpleCoder == null) { if (simpleCoder == null) {
od = createObjectDecoder(type); Type impl = formatObjectType(type);
od = createObjectDecoder(impl);
decoder = od; decoder = od;
} else { } else {
decoder = simpleCoder; decoder = simpleCoder;
@@ -805,13 +852,19 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
try { try {
method.setAccessible(true); method.setAccessible(true);
simpleCoder = (Encodeable) method.invoke(null, this); simpleCoder = (Encodeable) method.invoke(null, this);
RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName());
RedkaleClassLoader.putReflectionMethod(clazz.getName(), method);
break; break;
} catch (Exception e) { } catch (Exception e) {
} }
} }
if (simpleCoder == null) { if (simpleCoder == null) {
oe = createObjectEncoder(type); Type impl = formatObjectType(type);
encoder = oe; encoder = createDyncEncoder(impl);
if (encoder == null) {
oe = createObjectEncoder(impl);
encoder = oe;
}
} else { } else {
encoder = simpleCoder; encoder = simpleCoder;
} }

View File

@@ -0,0 +1,53 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 用于序列化时接口或抽象类的默认实现类, 被标记的类必须是接口或抽象类 <br>
* 使用场景: <br>
*
* <blockquote><pre>
* &#64;ConvertImpl(OneImpl.class)
* public interface OneEntity {
* 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);
* //one instanceof OneImpl
*
* </pre></blockquote>
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.5.0
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface ConvertImpl {
/**
* 默认的实现类
*
* @return String
*/
Class value();
}

View File

@@ -13,9 +13,9 @@ package org.redkale.convert;
* *
* @author zhangjx * @author zhangjx
* *
* @since 2.2.0 * @since 2.5.0
*/ */
public interface ConvertLoader { public interface ConvertProvider {
public ConvertType type(); public ConvertType type();

View File

@@ -0,0 +1,27 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 序列化时标记String字段的值是否为无转义字符且长度不超过255的字符串
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*
*/
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertSmallString {
}

Some files were not shown because too many files have changed in this diff Show More