3 Commits
2.3.0 ... 2.5.0

Author SHA1 Message Date
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
526 changed files with 34858 additions and 23267 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
hs_err_pid*
/target/

View File

@@ -1,6 +1,6 @@
<h1>项目介绍</h1>
<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>
<strong>RedKale 有如下主要特点:</strong>
<ol>

View File

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

View File

@@ -1,6 +1,5 @@
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence version="2.0">
<persistence-unit name="" transaction-type="RESOURCE_LOCAL">
<shared-cache-mode>ALL</shared-cache-mode>

View File

@@ -4,13 +4,30 @@
<groupId>org.redkale</groupId>
<artifactId>redkale</artifactId>
<packaging>jar</packaging>
<name>RedkaleProject</name>
<url>http://redkale.org</url>
<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>
<license>
<name>Apache 2</name>
<url>http://www.apache.org/licenses/</url>
<url>https://www.apache.org/licenses/</url>
<distribution>repo</distribution>
<comments>Apache License</comments>
</license>
@@ -21,13 +38,13 @@
<id>Redkale</id>
<name>redkale</name>
<email>redkale@qq.com</email>
<url>http://redkale.org</url>
<url>https://redkale.org</url>
<roles>
<role>Project Manager</role>
<role>Architect</role>
</roles>
<organization>redkale</organization>
<organizationUrl>http://redkale.org</organizationUrl>
<organizationUrl>https://redkale.org</organizationUrl>
<properties>
<dept>No</dept>
</properties>
@@ -35,12 +52,6 @@
</developer>
</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>
<snapshotRepository>
<id>ossrh</id>
@@ -51,11 +62,13 @@
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<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>
@@ -99,6 +112,7 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
@@ -111,6 +125,7 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>

View File

@@ -21,6 +21,7 @@
<profile>
<id>release</id>
<!--
<build>
<plugins>
<plugin>
@@ -92,6 +93,7 @@
</plugin>
</plugins>
</build>
-->
</profile>
</profiles>
</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.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>
<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>
<!--
【节点全局唯一】 @since 2.3.0
全局Serivce执行的线程池 Application.workExecutor, 没配置该节点将自动创建一个。
threads 线程数为0表示不启用workExecutor只用IO线程。默认: CPU核数, 核数=1的情况下默认值为2
hash: 是否使用ThreadHashExecutor作为线程池默认值为false
-->
<executor threads="4" hash="false"/>
<!--
【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
@@ -128,12 +136,12 @@
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
load: 加载文件,多个用;隔开。
默认置入的system.property.的有:
System.setProperty("net.transport.poolmaxconns", "100");
System.setProperty("net.transport.pinginterval", "30");
System.setProperty("net.transport.checkinterval", "30");
System.setProperty("convert.tiny", "true");
System.setProperty("convert.pool.size", "128");
System.setProperty("convert.writer.buffer.defsize", "4096");
System.setProperty("redkale.net.transport.poolmaxconns", "100");
System.setProperty("redkale.net.transport.pinginterval", "30");
System.setProperty("redkale.net.transport.checkinterval", "30");
System.setProperty("redkale.convert.tiny", "true");
System.setProperty("redkale.convert.pool.size", "128");
System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
<properties>节点下也可包含非<property>节点.
<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
@@ -156,30 +164,38 @@
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程数, 默认: CPU核数*2最小8个
threads【已废弃】 线程数, 默认: CPU核数*2最小8个【已废弃 @since 2.3.0】
maxconns 最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2
responsePoolSize Response池的大小默认: 1024
aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive
readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
-->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
<!--
【节点在<server>中唯一】
value: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLCreator的子类
clientauth: true/false/want
keystorepass: KEY密码
keystorefile: KEY文件
truststorepass: TRUST密码
truststorefile: TRUST文件
builder: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLBuilder的子类
sslProvider: java.security.Provider自定义的实现类如第三方: org.conscrypt.OpenSSLProvider、org.bouncycastle.jce.provider.BouncyCastleProvider
jsseProvider: java.security.Provider自定义的实现类如第三方: org.conscrypt.JSSEProvider、 org.bouncycastle.jce.provider.BouncyCastleJsseProvider
protocol: TLS版本默认值: TLS
protocols: 设置setEnabledProtocols, 多个用,隔开 如: TLSv1.2,TLSv1.3
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服务;
@@ -288,7 +304,7 @@
period>0表示定时获取时间; 设置1000表示每秒刷新Date时间
-->
<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=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/>
@@ -300,8 +316,9 @@
【节点在<server>中唯一】
当Server为HTTP协议时render才有效. 指定输出引擎的实现类
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
suffixs: 引擎文件名后缀,多个用;隔开,默认值为: .htel
-->
<render value="org.redkalex.htel.HttpTemplateRender"/>
<render value="org.redkalex.htel.HttpTemplateRender" suffixs=".htel"/>
<!--
【节点在<server>中唯一】
当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点

View File

@@ -12,28 +12,18 @@
是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存, 非NONE字样统一视为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的子类。
为了兼容用户习惯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"/>
是否自动建表当表不存在的时候, 目前只支持mysql、postgres 默认为false
-->
<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.password" value="123456"/>
<!-- 最大连接数默认值CPU数*16 -->
<property name="javax.persistence.connections.limit" value="32"/>
<!-- 最大连接数默认值CPU数 -->
<property name="javax.persistence.connections.limit" value="12"/>
<!-- 包含的SQL模板相当于反向LIKE不同的JDBC驱动的SQL语句不一样Redkale内置了MySQL的语句 -->
<property name="javax.persistence.contain.sqltemplate" value="LOCATE(${keystr}, ${column}) > 0"/>
@@ -41,7 +31,7 @@
<!-- 复制表结构的SQL模板Redkale内置了MySQL的语句 -->
<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>
</persistence-unit>

View File

@@ -29,5 +29,11 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Priority {
/**
* 优先级值
*
* @return int
*/
int value();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -102,9 +102,6 @@ public abstract class AnnotationVisitor {
* method calls. May be null.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.av = av;
}

View File

@@ -286,8 +286,13 @@ public class Attribute {
//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");
}
@@ -321,11 +326,16 @@ public class Attribute {
}
}
/**
*
*/
public static class NestHost extends Attribute {
byte[] bytes;
String clazz;
/**
*
*/
public NestHost() {
super("NestHost");
}

View File

@@ -104,9 +104,6 @@ public abstract class ClassVisitor {
* calls. May be null.
*/
public ClassVisitor(final int api, final ClassVisitor cv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.cv = cv;
}
@@ -244,9 +241,6 @@ public abstract class ClassVisitor {
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}

View File

@@ -1297,38 +1297,6 @@ public class ClassWriter extends ClassVisitor {
return result;
}
/**
* Adds a handle to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.</i>
*
* @param tag
* the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
* {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
* {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the field or method owner class.
* @param name
* the name of the field or method.
* @param desc
* the descriptor of the field or method.
* @return the index of a new or already existing method type reference
* item.
*
* @deprecated this method is superseded by
* {@link #newHandle(int, String, String, String, boolean)}.
*/
@Deprecated
public int newHandle(final int tag, final String owner, final String name,
final String desc) {
return newHandle(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
/**
* Adds a handle to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is

View File

@@ -101,9 +101,6 @@ public abstract class FieldVisitor {
* calls. May be null.
*/
public FieldVisitor(final int api, final FieldVisitor fv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.fv = fv;
}
@@ -145,9 +142,6 @@ public abstract class FieldVisitor {
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (fv != null) {
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}

View File

@@ -99,35 +99,6 @@ public final class Handle {
*/
final boolean itf;
/**
* Constructs a new field or method handle.
*
* @param tag
* the kind of field or method designated by this Handle. Must be
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
* {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the class that owns the field or method
* designated by this handle.
* @param name
* the name of the field or method designated by this handle.
* @param desc
* the descriptor of the field or method designated by this
* handle.
*
* @deprecated this constructor has been superseded
* by {@link #Handle(int, String, String, String, boolean)}.
*/
@Deprecated
public Handle(int tag, String owner, String name, String desc) {
this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
/**
* Constructs a new field or method handle.
*

View File

@@ -5,7 +5,10 @@
*/
package org.redkale.asm;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import static org.redkale.asm.Opcodes.*;
/**
* MethodVisitor 的调试类
@@ -54,11 +57,20 @@ public class MethodDebugVisitor {
}
}
/**
*
* @param visitor MethodVisitor
*/
public MethodDebugVisitor(MethodVisitor visitor) {
//super(Opcodes.ASM5, 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) {
AnnotationVisitor av = visitor.visitParameterAnnotation(i, string, bln);
if (debug) System.out.println("mv.visitParameterAnnotation(" + i + ", \"" + string + "\", " + bln + ");");
@@ -189,4 +201,74 @@ public class MethodDebugVisitor {
visitor.visitEnd();
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

@@ -118,9 +118,6 @@ public abstract class MethodVisitor {
* calls. May be null.
*/
public MethodVisitor(final int api, final MethodVisitor mv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.mv = mv;
}
@@ -140,9 +137,6 @@ public abstract class MethodVisitor {
* allowed (see {@link Opcodes}).
*/
public void visitParameter(String name, int access) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
mv.visitParameter(name, access);
}
@@ -209,9 +203,6 @@ public abstract class MethodVisitor {
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
@@ -453,35 +444,6 @@ public abstract class MethodVisitor {
}
}
/**
* Visits a method instruction. A method instruction is an instruction that
* invokes a method.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
* INVOKEINTERFACE.
* @param owner
* the internal name of the method's owner class (see
* {@link Type#getInternalName() getInternalName}).
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
*/
@Deprecated
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
if (api >= Opcodes.ASM5) {
boolean itf = opcode == Opcodes.INVOKEINTERFACE;
visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
}
/**
* Visits a method instruction. A method instruction is an instruction that
* invokes a method.
@@ -502,14 +464,6 @@ public abstract class MethodVisitor {
*/
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
if (api < Opcodes.ASM5) {
if (itf != (opcode == Opcodes.INVOKEINTERFACE)) {
throw new IllegalArgumentException(
"INVOKESPECIAL/STATIC on interfaces require ASM 5");
}
visitMethodInsn(opcode, owner, name, desc);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
@@ -723,9 +677,6 @@ public abstract class MethodVisitor {
*/
public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
}
@@ -783,9 +734,6 @@ public abstract class MethodVisitor {
*/
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
}
@@ -854,9 +802,6 @@ public abstract class MethodVisitor {
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
end, index, desc, visible);

View File

@@ -73,10 +73,9 @@ package org.redkale.asm;
public interface Opcodes {
// ASM API versions
int ASM4 = 4 << 16 | 0 << 8 | 0;
int ASM5 = 5 << 16 | 0 << 8 | 0;
int ASM6 = 6 << 16 | 0 << 8 | 0;
int ASM4 = 4 << 16 | 0 << 8;
int ASM5 = 5 << 16 | 0 << 8;
int ASM6 = 6 << 16 | 0 << 8;
// versions

View File

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

@@ -13,14 +13,14 @@ import java.lang.reflect.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import javax.annotation.Resource;
import javax.annotation.*;
import javax.net.ssl.SSLContext;
import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.*;
import org.redkale.convert.Convert;
@@ -32,10 +32,10 @@ import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.source.*;
import static org.redkale.source.DataSources.DATASOURCE_CONFPATH;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*;
import org.redkale.watch.*;
import org.w3c.dom.*;
/**
*
@@ -70,7 +70,8 @@ public final class Application {
public static final String RESNAME_APP_HOME = "APP_HOME";
/**
* 当前进程的配置目录如果不是绝对路径则视为HOME目录下的相对路径 类型StringFilePathURI
* 当前进程的配置目录URI如果不是绝对路径则视为HOME目录下的相对路径 类型StringURIFilePath <br>
* 若配置目录不是本地文件夹 则FilePath类型的值为null
*/
public static final String RESNAME_APP_CONF = "APP_CONF";
@@ -101,7 +102,7 @@ public final class Application {
*
* @since 2.3.0
*/
public static final String RESNAME_APP_GROUP = "APP_GROUP";
public static final String RESNAME_APP_ASYNCGROUP = "APP_ASYNCGROUP";
/**
* 当前Service所属的SNCP Server的地址 类型: SocketAddressInetSocketAddressString <br>
@@ -118,14 +119,6 @@ public final class Application {
*/
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
/**
* 当前Server的线程池
*
* @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
*/
@Deprecated
public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2;
/**
* 当前Server的ResourceFactory
*/
@@ -168,11 +161,14 @@ public final class Application {
final MessageAgent[] messageAgents;
//全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root();
final ResourceFactory resourceFactory = ResourceFactory.create();
//服务配置项
final AnyValue config;
//是否从/META-INF中读取配置
final boolean configFromCache;
//排除的jar路径
final String excludelibs;
@@ -184,7 +180,10 @@ public final class Application {
//--------------------------------------------------------------------------------------------
//是否用于main方法运行
final boolean singletonrun;
private final boolean singletonMode;
//是否用于编译模式运行
private final boolean compileMode;
//根WatchFactory
//private final WatchFactory watchFactory = WatchFactory.root();
@@ -198,7 +197,7 @@ public final class Application {
private final Logger logger;
//监听事件
private final List<ApplicationListener> listeners = new CopyOnWriteArrayList<>();
final List<ApplicationListener> listeners = new CopyOnWriteArrayList<>();
//服务启动时间
private final long startTime = System.currentTimeMillis();
@@ -212,13 +211,16 @@ public final class Application {
//Server根ClassLoader
private final RedkaleClassLoader serverClassLoader;
private Application(final AnyValue config) {
this(false, config);
Application(final AnyValue config) {
this(false, false, config);
}
private Application(final boolean singletonrun, final AnyValue config) {
this.singletonrun = singletonrun;
@SuppressWarnings("UseSpecificCatch")
Application(final boolean singletonMode, boolean compileMode, final AnyValue config) {
this.singletonMode = singletonMode;
this.compileMode = compileMode;
this.config = config;
this.configFromCache = "true".equals(config.getValue("[config-from-cache]"));
System.setProperty("redkale.version", Redkale.getDotedVersion());
final File root = new File(System.getProperty(RESNAME_APP_HOME));
@@ -226,16 +228,25 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath());
this.resourceFactory.register(RESNAME_APP_HOME, File.class, root);
this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI());
File confFile = null;
try {
this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
if (System.getProperty(RESNAME_APP_HOME) == null) {
System.setProperty(RESNAME_APP_HOME, root.getCanonicalPath());
}
this.home = root.getCanonicalFile();
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
if (confsubpath.contains("://")) {
this.confPath = new URI(confsubpath);
} else if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) {
this.confPath = new File(confsubpath).getCanonicalFile().toURI();
String confDir = System.getProperty(RESNAME_APP_CONF, "conf");
if (confDir.contains("://") || confDir.startsWith("file:") || confDir.startsWith("resource:") || confDir.contains("!")) { //graalvm native-image startwith resource:META-INF
this.confPath = new URI(confDir);
if (confDir.startsWith("file:")) {
confFile = new File(this.confPath.getPath()).getCanonicalFile();
}
} else if (confDir.charAt(0) == '/' || confDir.indexOf(':') > 0) {
confFile = new File(confDir).getCanonicalFile();
this.confPath = confFile.toURI();
} else {
this.confPath = new File(this.home, confsubpath).getCanonicalFile().toURI();
confFile = new File(this.home, confDir).getCanonicalFile();
this.confPath = confFile.toURI();
}
} catch (Exception e) {
throw new RuntimeException(e);
@@ -247,6 +258,13 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, addr);
this.resourceFactory.register(RESNAME_APP_ADDR, InetSocketAddress.class, this.localAddress);
this.resourceFactory.register(RESNAME_APP_CONF, URI.class, this.confPath);
this.resourceFactory.register(RESNAME_APP_CONF, String.class, this.confPath.toString());
if (confFile != null) {
this.resourceFactory.register(RESNAME_APP_CONF, File.class, confFile);
this.resourceFactory.register(RESNAME_APP_CONF, Path.class, confFile.toPath());
}
{
int nid = config.getIntValue("nodeid", 0);
this.nodeid = nid;
@@ -258,59 +276,120 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_NAME, name);
System.setProperty(RESNAME_APP_NAME, name);
}
//以下是初始化日志配置
final URI logConfURI = "file".equals(confPath.getScheme()) ? new File(new File(confPath), "logging.properties").toURI()
: URI.create(confPath.toString() + (confPath.toString().endsWith("/") ? "" : "/") + "logging.properties");
if (!"file".equals(confPath.getScheme()) || (new File(logConfURI).isFile() && new File(logConfURI).canRead())) {
try {
final String rootpath = root.getCanonicalPath().replace('\\', '/');
InputStream fin = logConfURI.toURL().openStream();
Properties properties = new Properties();
properties.load(fin);
fin.close();
properties.entrySet().stream().forEach(x -> {
x.setValue(x.getValue().toString().replace("${APP_HOME}", rootpath));
});
if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) {
properties.setProperty("java.util.logging.FileHandler.formatter", LogFileHandler.LoggingFormater.class.getName());
}
if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) {
properties.setProperty("java.util.logging.ConsoleHandler.formatter", LogFileHandler.LoggingFormater.class.getName());
}
String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern");
if (fileHandlerPattern != null && fileHandlerPattern.contains("%d")) {
final String fileHandlerClass = LogFileHandler.class.getName();
Properties prop = new Properties();
final String handlers = properties.getProperty("handlers");
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
//singletonrun模式下不输出文件日志
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonrun ? "" : fileHandlerClass));
{
ClassLoader currClassLoader = Thread.currentThread().getContextClassLoader();
if (currClassLoader instanceof RedkaleClassLoader) {
this.classLoader = (RedkaleClassLoader) currClassLoader;
} else {
Set<String> cacheClasses = null;
if (!singletonMode && !compileMode) {
try {
InputStream in = Application.class.getResourceAsStream(RedkaleClassLoader.RESOURCE_CACHE_CLASSES_PATH);
if (in != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8), 1024);
List<String> list = new ArrayList<>();
reader.lines().forEach(v -> list.add(v));
Collections.sort(list);
if (!list.isEmpty()) cacheClasses = new LinkedHashSet<>(list);
in.close();
}
} catch (Exception e) {
}
if (!prop.isEmpty()) {
String prefix = fileHandlerClass + ".";
properties.entrySet().stream().forEach(x -> {
if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) {
prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue());
}
if (cacheClasses == null) {
this.classLoader = new RedkaleClassLoader(currClassLoader);
} else {
this.classLoader = new RedkaleClassLoader.RedkaleCacheClassLoader(currClassLoader, cacheClasses);
}
Thread.currentThread().setContextClassLoader(this.classLoader);
}
}
//以下是初始化日志配置
{
URI logConfURI;
File logConfFile = null;
if (configFromCache) {
logConfURI = RedkaleClassLoader.getConfResourceAsURI(null, "logging.properties");
} else if ("file".equals(confPath.getScheme())) {
logConfFile = new File(confPath.getPath(), "logging.properties");
logConfURI = logConfFile.toURI();
if (!logConfFile.isFile() || !logConfFile.canRead()) logConfFile = null;
} else {
logConfURI = URI.create(confPath + (confPath.toString().endsWith("/") ? "" : "/") + "logging.properties");
}
if (!"file".equals(confPath.getScheme()) || logConfFile != null) {
try {
final String rootpath = root.getCanonicalPath().replace('\\', '/');
InputStream fin = logConfURI.toURL().openStream();
Properties properties = new Properties();
properties.load(fin);
fin.close();
properties.entrySet().forEach(x -> x.setValue(x.getValue().toString().replace("${APP_HOME}", rootpath)));
if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) {
if (compileMode) {
properties.setProperty("java.util.logging.FileHandler.formatter", SimpleFormatter.class.getName());
if (properties.getProperty("java.util.logging.SimpleFormatter.format") == null) {
properties.setProperty("java.util.logging.SimpleFormatter.format", LoggingFileHandler.FORMATTER_FORMAT.replaceAll("\r\n", "%n"));
}
} else {
properties.setProperty("java.util.logging.FileHandler.formatter", LoggingFileHandler.LoggingFormater.class.getName());
}
}
if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) {
if (compileMode) {
properties.setProperty("java.util.logging.ConsoleHandler.formatter", SimpleFormatter.class.getName());
if (properties.getProperty("java.util.logging.SimpleFormatter.format") == null) {
properties.setProperty("java.util.logging.SimpleFormatter.format", LoggingFileHandler.FORMATTER_FORMAT.replaceAll("\r\n", "%n"));
}
} else {
properties.setProperty("java.util.logging.ConsoleHandler.formatter", LoggingFileHandler.LoggingFormater.class.getName());
}
}
String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern");
if (fileHandlerPattern != null && fileHandlerPattern.contains("%d")) {
final String fileHandlerClass = LoggingFileHandler.class.getName();
Properties prop = new Properties();
final String handlers = properties.getProperty("handlers");
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
//singletonrun模式下不输出文件日志
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonMode || compileMode ? "" : fileHandlerClass));
}
if (!prop.isEmpty()) {
String prefix = fileHandlerClass + ".";
properties.entrySet().forEach(x -> {
if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) {
prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue());
}
});
prop.entrySet().forEach(x -> {
properties.put(x.getKey(), x.getValue());
});
}
if (!compileMode) {
properties.put(SncpClient.class.getSimpleName() + ".handlers", LoggingFileHandler.LoggingSncpFileHandler.class.getName());
}
}
if (compileMode) {
properties.put("handlers", "java.util.logging.ConsoleHandler");
Map newprop = new HashMap(properties);
newprop.forEach((k, v) -> {
if (k.toString().startsWith("java.util.logging.FileHandler.")) {
properties.remove(k);
}
});
prop.entrySet().stream().forEach(x -> {
properties.put(x.getKey(), x.getValue());
});
}
properties.put(SncpClient.class.getSimpleName() + ".handlers", LogFileHandler.SncpLogFileHandler.class.getName());
ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(out);
properties.forEach((x, y) -> ps.println(x + "=" + y));
LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
} catch (Exception e) {
Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(out);
properties.forEach((x, y) -> ps.println(x + "=" + y));
LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
} catch (Exception e) {
Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e);
}
}
this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------");
//------------------配置 <transport> 节点 ------------------
final AnyValue resources = config.getAnyValue("resources");
@@ -319,7 +398,7 @@ public final class Application {
ClusterAgent cluster = null;
MessageAgent[] mqs = null;
int bufferCapacity = 32 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int bufferPoolSize = Utility.cpus() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AnyValue executorConf = null;
@@ -332,10 +411,10 @@ public final class Application {
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
if (transportConf != null) {
//--------------transportBufferPool-----------
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 8 * 1024);
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 32 * 1024);
readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds);
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Utility.cpus() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
}
@@ -344,18 +423,21 @@ public final class Application {
try {
String classval = clusterConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<ClusterAgent> it = ServiceLoader.load(ClusterAgent.class, classLoader).iterator();
Iterator<ClusterAgentProvider> it = ServiceLoader.load(ClusterAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(ClusterAgentProvider.class);
while (it.hasNext()) {
ClusterAgent agent = it.next();
if (agent.match(clusterConf)) {
cluster = agent;
ClusterAgentProvider agent = it.next();
if (agent != null) RedkaleClassLoader.putReflectionPublicConstructors(agent.getClass(), agent.getClass().getName()); //loader class
if (agent != null && agent.acceptsConf(clusterConf)) {
RedkaleClassLoader.putReflectionPublicConstructors(agent.getClass(), agent.agentClass().getName()); //agent class
cluster = agent.agentClass().getConstructor().newInstance();
cluster.setConfig(clusterConf);
break;
}
}
if (cluster == null) {
ClusterAgent cacheClusterAgent = new CacheClusterAgent();
if (cacheClusterAgent.match(clusterConf)) {
if (cacheClusterAgent.acceptsConf(clusterConf)) {
cluster = cacheClusterAgent;
cluster.setConfig(clusterConf);
}
@@ -366,6 +448,7 @@ public final class Application {
if (!ClusterAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance();
cluster.setConfig(clusterConf);
}
@@ -380,7 +463,7 @@ public final class Application {
mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[0];
AnyValue mqConf = mqConfs[i];
String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
mqnames.add(mqname);
@@ -395,11 +478,14 @@ public final class Application {
try {
String classval = mqConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<MessageAgent> it = ServiceLoader.load(MessageAgent.class, classLoader).iterator();
Iterator<MessageAgentProvider> it = ServiceLoader.load(MessageAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(MessageAgentProvider.class);
while (it.hasNext()) {
MessageAgent messageAgent = it.next();
if (messageAgent.match(mqConf)) {
mqs[i] = messageAgent;
MessageAgentProvider messageAgent = it.next();
if (messageAgent != null) RedkaleClassLoader.putReflectionPublicConstructors(messageAgent.getClass(), messageAgent.getClass().getName()); //loader class
if (messageAgent != null && messageAgent.acceptsConf(mqConf)) {
RedkaleClassLoader.putReflectionPublicConstructors(messageAgent.getClass(), messageAgent.agentClass().getName()); //agent class
mqs[i] = messageAgent.agentClass().getConstructor().newInstance();
mqs[i].setConfig(mqConf);
break;
}
@@ -410,6 +496,7 @@ public final class Application {
if (!MessageAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance();
mqs[i].setConfig(mqConf);
}
@@ -424,22 +511,24 @@ public final class Application {
ExecutorService workExecutor0 = null;
if (executorConf != null) {
final AtomicReference<ExecutorService> workref = new AtomicReference<>();
int executorThreads = executorConf.getIntValue("threads", Math.max(2, Runtime.getRuntime().availableProcessors()));
final int executorThreads = executorConf.getIntValue("threads", Math.max(2, Utility.cpus()));
boolean executorHash = executorConf.getBoolValue("hash");
if (executorThreads > 0) {
final AtomicInteger workcounter = new AtomicInteger();
if (executorHash) {
workExecutor0 = new ThreadHashExecutor(executorThreads, (Runnable r) -> {
int i = workcounter.get();
int c = workcounter.incrementAndGet();
String threadname = "Redkale-HashWorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
Thread t = new WorkThread(threadname, i, executorThreads, workref.get(), r);
return t;
});
} else {
workExecutor0 = Executors.newFixedThreadPool(executorThreads, (Runnable r) -> {
int i = workcounter.get();
int c = workcounter.incrementAndGet();
String threadname = "Redkale-WorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
Thread t = new WorkThread(threadname, i, executorThreads, workref.get(), r);
return t;
});
}
@@ -450,23 +539,26 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor);
this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor);
this.asyncGroup = new AsyncIOGroup(this.workExecutor, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize);
this.resourceFactory.register(RESNAME_APP_GROUP, AsyncGroup.class, this.asyncGroup);
this.asyncGroup = new AsyncIOGroup(true, null, this.workExecutor, bufferCapacity, bufferPoolSize);
this.resourceFactory.register(RESNAME_APP_ASYNCGROUP, AsyncGroup.class, this.asyncGroup);
this.excludelibs = excludelib0;
this.sncpTransportFactory = TransportFactory.create(this.asyncGroup, (SSLContext) null, Transport.DEFAULT_NETPROTOCOL, readTimeoutSeconds, writeTimeoutSeconds, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("redkale.net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("redkale.net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("redkale.net.transport.check.interval", "30"));
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
this.clusterAgent = cluster;
this.messageAgents = mqs;
Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
if (compileMode || this.classLoader instanceof RedkaleClassLoader.RedkaleCacheClassLoader) {
this.serverClassLoader = this.classLoader;
} else {
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
}
}
private String checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return name;
if (name == null || name.isEmpty()) return name;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
@@ -552,34 +644,54 @@ public final class Application {
return config;
}
public void init() throws Exception {
System.setProperty("net.transport.poolmaxconns", "100");
System.setProperty("net.transport.pinginterval", "30");
System.setProperty("net.transport.checkinterval", "30");
System.setProperty("convert.tiny", "true");
System.setProperty("convert.pool.size", "128");
System.setProperty("convert.writer.buffer.defsize", "4096");
public boolean isCompileMode() {
return compileMode;
}
final String confpath = this.confPath.toString();
public boolean isSingletonMode() {
return singletonMode;
}
public void init() throws Exception {
System.setProperty("redkale.net.transport.poolmaxconns", "100");
System.setProperty("redkale.net.transport.pinginterval", "30");
System.setProperty("redkale.net.transport.checkinterval", "30");
System.setProperty("redkale.convert.tiny", "true");
System.setProperty("redkale.convert.pool.size", "128");
System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
final String confDir = this.confPath.toString();
final String homepath = this.home.getCanonicalPath();
if ("file".equals(this.confPath.getScheme())) {
File persist = new File(new File(confPath), "persistence.xml");
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
} else {
System.setProperty(DataSources.DATASOURCE_CONFPATH, confpath + (confpath.endsWith("/") ? "" : "/") + "persistence.xml");
System.setProperty(DataSources.DATASOURCE_CONFPATH, confDir + (confDir.endsWith("/") ? "" : "/") + "persistence.xml");
}
String pidstr = "";
try { //JDK 9+
Class phclass = Class.forName("java.lang.ProcessHandle");
Object phobj = phclass.getMethod("current").invoke(null);
Object pid = phclass.getMethod("pid").invoke(phobj);
pidstr = "APP_PID = " + pid + "\r\n";
} catch (Throwable t) {
// String pidstr = "";
// try { //JDK 9+
// Class phclass = Thread.currentThread().getContextClassLoader().loadClass("java.lang.ProcessHandle");
// Object phobj = phclass.getMethod("current").invoke(null);
// Object pid = phclass.getMethod("pid").invoke(phobj);
// pidstr = "APP_PID = " + pid + "\r\n";
// } catch (Throwable t) {
// }
logger.log(Level.INFO, "APP_OSNAME = " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch") + "\r\n"
+ "APP_JAVA = " + System.getProperty("java.runtime.name", System.getProperty("org.graalvm.nativeimage.kind") != null ? "Nativeimage" : "")
+ " " + System.getProperty("java.runtime.version", System.getProperty("java.vendor.version", System.getProperty("java.vm.version"))) + "\r\n" //graalvm.nativeimage 模式下无 java.runtime.xxx 属性
+ "APP_PID = " + ProcessHandle.current().pid() + "\r\n"
+ RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n"
+ "APP_LOADER = " + this.classLoader.getClass().getSimpleName() + "\r\n"
+ RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n"
+ RESNAME_APP_HOME + " = " + homepath + "\r\n"
+ RESNAME_APP_CONF + " = " + confDir.substring(confDir.indexOf('!') + 1));
if (!compileMode && !(classLoader instanceof RedkaleClassLoader.RedkaleCacheClassLoader)) {
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? confDir : (lib + ";" + confDir);
Server.loadLib(classLoader, logger, lib);
}
logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath);
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? confpath : (lib + ";" + confpath);
Server.loadLib(classLoader, logger, lib);
//------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources");
@@ -591,8 +703,8 @@ public final class Application {
if (dfloads != null) {
for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue;
final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI();
if (!"file".equals(df.getScheme()) || new File(df).isFile()) {
final URI df = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : confDir, dfload.trim());
if (df != null && (!"file".equals(df.getScheme()) || df.toString().contains("!") || new File(df).isFile())) {
Properties ps = new Properties();
try {
InputStream in = df.toURL().openStream();
@@ -649,7 +761,7 @@ public final class Application {
} else if (type == NodeSncpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeSncpServer.class) continue;
if (ns.getClass() != NodeSncpServer.class) continue;
if (res.name().equals(ns.server.getName())) {
server = ns;
break;
@@ -659,7 +771,7 @@ public final class Application {
} else if (type == NodeHttpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeHttpServer.class) continue;
if (ns.getClass() != NodeHttpServer.class) continue;
if (res.name().equals(ns.server.getName())) {
server = ns;
break;
@@ -669,7 +781,7 @@ public final class Application {
} else if (type == NodeWatchServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeWatchServer.class) continue;
if (ns.getClass() != NodeWatchServer.class) continue;
if (res.name().equals(ns.server.getName())) {
server = ns;
break;
@@ -738,7 +850,13 @@ public final class Application {
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
if (clusterAgent == null) return;
if (clusterAgent == null) {
HttpMessageClient messageClient = new HttpMessageLocalClient(application, resourceName);
field.set(src, messageClient);
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
return;
}
HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent);
field.set(src, messageClient);
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
@@ -758,22 +876,36 @@ public final class Application {
try {
Class sourceType = CacheMemorySource.class;
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class);
List<CacheSourceProvider> providers = new ArrayList<>();
Iterator<CacheSourceProvider> it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
sourceType = s.getClass();
break;
CacheSourceProvider s = it.next();
if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName());
if (s != null && s.acceptsConf(sourceConf)) {
providers.add(s);
}
}
Collections.sort(providers, (a, b) -> {
Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class);
Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class);
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
});
for (CacheSourceProvider provider : providers) {
sourceType = provider.sourceClass();
if (sourceType != null) break;
}
} else {
sourceType = serverClassLoader.loadClass(classval);
}
CacheSource source = Modifier.isFinal(sourceType.getModifiers()) ? (CacheSource) sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, sourceName, sourceType, null, resourceFactory, sncpTransportFactory, null, null, sourceConf);
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
CacheSource source = sourceType == CacheMemorySource.class ? new CacheMemorySource(sourceName)
: Modifier.isFinal(sourceType.getModifiers()) ? (CacheSource) sourceType.getConstructor().newInstance()
: (CacheSource) Sncp.createLocalService(serverClassLoader, sourceName, sourceType, null, resourceFactory, sncpTransportFactory, null, null, sourceConf);
cacheSources.add((CacheSource) source);
resourceFactory.register(sourceName, CacheSource.class, source);
resourceFactory.inject(source);
if (source instanceof Service) ((Service) source).init(sourceConf);
if (!compileMode && source instanceof Service) ((Service) source).init(sourceConf);
logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + sourceName + ", source = " + source);
} catch (Exception e) {
logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e);
@@ -785,6 +917,9 @@ public final class Application {
private void initResources() throws Exception {
//-------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources");
if (!compileMode && !singletonMode && configFromCache) {
System.setProperty(DATASOURCE_CONFPATH, ""); //必须要清空
}
if (resources != null) {
//------------------------------------------------------------------------
for (AnyValue conf : resources.getAnyValues("group")) {
@@ -805,6 +940,7 @@ public final class Application {
if (listenClass.isEmpty()) continue;
Class clazz = classLoader.loadClass(listenClass);
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
@SuppressWarnings("unchecked")
ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(listener);
@@ -837,7 +973,8 @@ public final class Application {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
final String cmd = new String(bytes);
String[] param = JsonConvert.root().convertFrom(String[].class, bytes);
final String cmd = param[0];
if ("SHUTDOWN".equalsIgnoreCase(cmd)) {
try {
long s = System.currentTimeMillis();
@@ -860,7 +997,9 @@ public final class Application {
}
} else if ("APIDOC".equalsIgnoreCase(cmd)) {
try {
new ApiDocsService(application).run();
String[] args = new String[param.length - 1];
if (param.length > 1) System.arraycopy(param, 1, args, 0, args.length);
new ApiDocsService(application).run(args);
buffer.clear();
buffer.put("APIDOC OK".getBytes());
buffer.flip();
@@ -891,12 +1030,15 @@ public final class Application {
}.start();
}
private static void sendCommand(Logger logger, int port, String command) throws Exception {
private static void sendCommand(Logger logger, int port, String command, String[] args) throws Exception {
final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(new InetSocketAddress("127.0.0.1", port));
ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.put(command.getBytes());
ByteBuffer buffer = ByteBuffer.allocate(1024);
String[] param = new String[1 + (args == null ? 0 : args.length)];
param[0] = command;
if (args != null) System.arraycopy(args, 0, param, 1, args.length);
buffer.put(JsonConvert.root().convertToBytes(param));
buffer.flip();
channel.write(buffer);
buffer.clear();
@@ -915,7 +1057,7 @@ public final class Application {
final Application application = Application.create(true);
application.init();
application.start();
new ApiDocsService(application).run();
new ApiDocsService(application).run(args);
if (logger != null) logger.info("APIDOC OK");
return;
}
@@ -925,7 +1067,7 @@ public final class Application {
}
public void start() throws Exception {
if (!singletonrun && this.clusterAgent != null) {
if (!singletonMode && !compileMode && this.clusterAgent != null) {
this.clusterAgent.register(this);
}
final AnyValue[] entrys = config.getAnyValues("server");
@@ -974,7 +1116,7 @@ public final class Application {
for (ApplicationListener listener : this.listeners) {
listener.postStart(this);
}
if (!singletonrun) this.serversLatch.await();
if (!singletonMode && !compileMode) this.serversLatch.await();
}
private static String alignString(String value, int maxlen) {
@@ -1063,7 +1205,7 @@ public final class Application {
synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, ((excludelibs != null ? (excludelibs + ";") : "") + serconf.getValue("excludelibs", "")).split(";"), profilter);
ClassFilter.Loader.load(home, classLoader, ((excludelibs != null ? (excludelibs + ";") : "") + serconf.getValue("excludelibs", "")).split(";"), profilter);
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) {
final Class<? extends NodeServer> type = entry.getType();
@@ -1088,7 +1230,11 @@ public final class Application {
}
servers.add(server);
server.init(serconf);
if (!singletonrun) server.start();
if (!singletonMode && !compileMode) {
server.start();
} else if (compileMode) {
server.getServer().getPrepareServlet().init(server.getServer().getContext(), serconf);
}
timecd.countDown();
sercdl.countDown();
} catch (Exception ex) {
@@ -1130,11 +1276,11 @@ public final class Application {
}
public static Application create(final boolean singleton) throws IOException {
return new Application(singleton, loadAppXml());
return new Application(singleton, false, loadAppConfig());
}
public void reloadConfig() throws IOException {
AnyValue newconfig = loadAppXml();
AnyValue newconfig = loadAppConfig();
final String confpath = this.confPath.toString();
final String homepath = this.home.getCanonicalPath();
final AnyValue resources = newconfig.getAnyValue("resources");
@@ -1179,30 +1325,61 @@ public final class Application {
}
}
private static AnyValue loadAppXml() throws IOException {
static AnyValue loadAppConfig() throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home);
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
URI appconf;
if (confsubpath.contains("://")) {
appconf = URI.create(confsubpath + (confsubpath.endsWith("/") ? "" : "/") + "application.xml");
} else if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) {
appconf = new File(confsubpath, "application.xml").toURI();
String confDir = System.getProperty(RESNAME_APP_CONF, "conf");
URI appConfFile;
boolean fromcache = false;
if (confDir.contains("://")) {
appConfFile = URI.create(confDir + (confDir.endsWith("/") ? "" : "/") + "application.xml");
} else if (confDir.charAt(0) == '/' || confDir.indexOf(':') > 0) {
File f = new File(confDir, "application.xml");
if (f.isFile() && f.canRead()) {
appConfFile = f.toURI();
confDir = f.getParentFile().getCanonicalPath();
} else {
appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.xml"); //不能传confDir
confDir = appConfFile.toString().replace("/application.xml", "");
fromcache = true;
}
} else {
appconf = new File(new File(home, confsubpath), "application.xml").toURI();
File f = new File(new File(home, confDir), "application.xml");
if (f.isFile() && f.canRead()) {
appConfFile = f.toURI();
confDir = f.getParentFile().getCanonicalPath();
} else {
appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.xml"); //不能传confDir
confDir = appConfFile.toString().replace("/application.xml", "");
fromcache = true;
}
}
return load(appconf.toURL().openStream());
System.setProperty(RESNAME_APP_CONF, confDir);
AnyValue conf = AnyValue.loadFromXml(appConfFile.toURL().openStream(), StandardCharsets.UTF_8, (k, v) -> v.replace("${APP_HOME}", home)).getAnyValue("application");
if (fromcache) ((DefaultAnyValue) conf).addValue("[config-from-cache]", "true");
return conf;
}
public static void main(String[] args) throws Exception {
Utility.midnight(); //先初始化一下Utility
Thread.currentThread().setName("Redkale-Application-Main-Thread");
//运行主程序
if (System.getProperty("CMD") != null) {
AnyValue config = loadAppXml();
Application.sendCommand(null, config.getIntValue("port"), System.getProperty("CMD"));
return;
{
String cmd = System.getProperty("cmd", System.getProperty("CMD"));
String[] params = args;
if (cmd == null && args != null && args.length > 0
&& args[0] != null && !args[0].trim().isEmpty() && !"start".equalsIgnoreCase(args[0])) {
cmd = args[0];
params = Arrays.copyOfRange(args, 1, args.length);
}
if (cmd != null) {
AnyValue config = loadAppConfig();
Application.sendCommand(null, config.getIntValue("port"), cmd, params);
return;
}
}
//PrepareCompiler.main(args); //测试代码
final Application application = Application.create(false);
application.init();
application.startSelfServer();
@@ -1248,7 +1425,7 @@ public final class Application {
}
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
Collections.reverse(localServers); //倒序 必须让watchs先关闭watch包含服务发现和注销逻辑
if (this.messageAgents != null) {
if (isCompileMode() && this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent stopping");
long s = System.currentTimeMillis();
@@ -1258,7 +1435,7 @@ public final class Application {
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") stop in " + (System.currentTimeMillis() - s) + " ms");
}
if (clusterAgent != null) {
if (!isCompileMode() && clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent destroying");
long s = System.currentTimeMillis();
clusterAgent.deregister(this);
@@ -1287,7 +1464,12 @@ public final class Application {
for (DataSource source : dataSources) {
if (source == null) continue;
try {
source.getClass().getMethod("close").invoke(source);
if (source instanceof Service) {
((Service) source).destroy(Sncp.isSncpDyn((Service) source) ? Sncp.getConf((Service) source) : null);
// } else {
// source.getClass().getMethod("close").invoke(source);
// RedkaleClassLoader.putReflectionMethod(source.getClass().getName(), source.getClass().getMethod("close"));
}
} catch (Exception e) {
logger.log(Level.FINER, source.getClass() + " close DataSource erroneous", e);
}
@@ -1295,11 +1477,19 @@ public final class Application {
for (CacheSource source : cacheSources) {
if (source == null) continue;
try {
source.getClass().getMethod("close").invoke(source);
if (source instanceof Service) {
((Service) source).destroy(Sncp.isSncpDyn((Service) source) ? Sncp.getConf((Service) source) : null);
// } else {
// source.getClass().getMethod("close").invoke(source);
// RedkaleClassLoader.putReflectionMethod(source.getClass().getName(), source.getClass().getMethod("close"));
}
} catch (Exception e) {
logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e);
}
}
if (this.asyncGroup != null) {
((AsyncIOGroup) this.asyncGroup).close();
}
this.sncpTransportFactory.shutdownNow();
}
@@ -1312,37 +1502,4 @@ public final class Application {
return Integer.decode(value);
}
private static AnyValue load(final InputStream in0) {
final DefaultAnyValue any = new DefaultAnyValue();
try (final InputStream in = in0) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(in);
Element root = doc.getDocumentElement();
load(any, root);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return any;
}
private static void load(final DefaultAnyValue any, final Node root) {
final String home = System.getProperty(RESNAME_APP_HOME);
NamedNodeMap nodes = root.getAttributes();
if (nodes == null) return;
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
any.addValue(node.getNodeName(), node.getNodeValue().replace("${APP_HOME}", home));
}
NodeList children = root.getChildNodes();
if (children == null) return;
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() != Node.ELEMENT_NODE) continue;
DefaultAnyValue sub = new DefaultAnyValue();
load(sub, node);
any.addValue(node.getNodeName(), sub);
}
}
}

View File

@@ -43,6 +43,22 @@ public interface ApplicationListener {
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前调用
*

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import java.net.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.stream.Stream;
import javax.annotation.*;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.boot.ClassFilter.FilterEntry;
@@ -151,6 +152,7 @@ public class NodeHttpServer extends NodeServer {
for (FilterEntry<? extends Filter> en : list) {
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
@@ -162,6 +164,9 @@ public class NodeHttpServer extends NodeServer {
@SuppressWarnings("unchecked")
protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
RedkaleClassLoader.putReflectionPublicClasses(HttpServlet.class.getName());
RedkaleClassLoader.putReflectionPublicClasses(HttpPrepareServlet.class.getName());
RedkaleClassLoader.putReflectionDeclaredConstructors(HttpResourceServlet.class, HttpResourceServlet.class.getName());
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", "");
@@ -181,16 +186,19 @@ public class NodeHttpServer extends NodeServer {
}
return ws1 ? -1 : 1;
});
final long starts = System.currentTimeMillis();
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
for (FilterEntry<? extends Servlet> en : list) {
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
if (clazz.getAnnotation(Rest.RestDyn.class) != null) continue; //动态生成的跳过
WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null) continue;
if (ws.value().length == 0) {
logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName());
continue;
}
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(servlet, this);
final String[] mappings = ws.value();
@@ -201,44 +209,96 @@ public class NodeHttpServer extends NodeServer {
for (int i = 0; i < mappings.length; i++) {
mappings[i] = pref + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
ss.add(new AbstractMap.SimpleEntry<>("HttpServlet (type=" + clazz.getName() + ")", mappings));
}
}
final List<AbstractMap.SimpleEntry<String, String[]>> rests = sb == null ? null : new ArrayList<>();
final List<AbstractMap.SimpleEntry<String, String[]>> webss = sb == null ? null : new ArrayList<>();
if (rest && serverConf != null) {
final List<Object> restedObjects = new ArrayList<>();
for (AnyValue restConf : serverConf.getAnyValues("rest")) {
loadRestServlet(webSocketFilter, restConf, restedObjects, sb, rests, webss);
}
}
int max = 0;
if (ss != null && sb != null) {
int maxTypeLength = 0;
int maxNameLength = 0;
if (rests != null) {
for (AbstractMap.SimpleEntry<String, String[]> en : rests) {
int pos = en.getKey().indexOf('#');
if (pos > maxTypeLength) maxTypeLength = pos;
int len = en.getKey().length() - pos - 1;
if (len > maxNameLength) maxNameLength = len;
}
}
if (webss != null) {
for (AbstractMap.SimpleEntry<String, String[]> en : webss) {
int pos = en.getKey().indexOf('#');
if (pos > maxTypeLength) maxTypeLength = pos;
int len = en.getKey().length() - pos - 1;
if (len > maxNameLength) maxNameLength = len;
}
}
if (rests != null) {
for (AbstractMap.SimpleEntry<String, String[]> en : rests) {
StringBuilder sub = new StringBuilder();
int pos = en.getKey().indexOf('#');
sub.append("RestDynServlet (type=").append(en.getKey().substring(0, pos));
for (int i = 0; i < maxTypeLength - pos; i++) {
sub.append(' ');
}
sub.append(", name='").append(en.getKey().substring(pos + 1));
for (int i = 0; i < maxNameLength - pos; i++) {
sub.append(' ');
}
sub.append("')");
ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue()));
}
}
if (webss != null) {
for (AbstractMap.SimpleEntry<String, String[]> en : webss) {
StringBuilder sub = new StringBuilder();
int pos = en.getKey().indexOf('#');
sub.append("RestWebSocket (type=").append(en.getKey().substring(0, pos));
for (int i = 0; i < maxTypeLength - pos; i++) {
sub.append(' ');
}
sub.append(", name='").append(en.getKey().substring(pos + 1));
for (int i = 0; i < maxNameLength - pos; i++) {
sub.append(' ');
}
sub.append("')");
ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue()));
}
}
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
if (as.getKey().length() > max) max = as.getKey().length();
}
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
sb.append(localThreadName).append(" Load ").append(as.getKey());
sb.append(localThreadName).append("Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' ');
}
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
}
}
if (rest && serverConf != null) {
final List<Object> restedObjects = new ArrayList<>();
for (AnyValue restConf : serverConf.getAnyValues("rest")) {
loadRestServlet(webSocketFilter, restConf, restedObjects, sb);
}
sb.append(localThreadName).append("All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
}
@SuppressWarnings("unchecked")
protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb) throws Exception {
protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb,
final List<AbstractMap.SimpleEntry<String, String[]>> rests, final List<AbstractMap.SimpleEntry<String, String[]>> webss) throws Exception {
if (!rest) return;
if (restConf == null) return; //不存在REST服务
final long starts = System.currentTimeMillis();
String prefix0 = restConf.getValue("path", "");
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
String mqname = restConf.getValue("mq");
MessageAgent agent0 = null;
if (mqname != null) {
@@ -264,10 +324,11 @@ public class NodeHttpServer extends NodeServer {
}
}
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
final boolean finest = logger.isLoggable(Level.FINEST);
final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues);
final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size());
super.interceptorServices.stream().parallel().forEach((service) -> {
Stream<Service> stream = super.interceptorServices.stream();
if (!application.isCompileMode()) stream = stream.parallel(); //不能并行否则在maven plugin运行环境下ClassLoader不对
stream.forEach((service) -> {
try {
final Class stype = Sncp.getServiceType(service);
final String name = Sncp.getResourceName(service);
@@ -293,13 +354,13 @@ public class NodeHttpServer extends NodeServer {
dynServletMap.put(service, servlet);
if (messageAgent != null) messageAgent.putService(this, service, servlet);
//if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) {
if (rests != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix2 + mappings[i];
}
synchronized (ss) {
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName() + "(rest.name='" + name + "')", mappings));
synchronized (rests) {
rests.add(new AbstractMap.SimpleEntry<>(Sncp.getServiceType(service).getName() + "#" + name, mappings));
}
}
} finally {
@@ -318,8 +379,7 @@ public class NodeHttpServer extends NodeServer {
includeValues.add(item.getValue("value", ""));
}
}
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues);
final boolean finest = logger.isLoggable(Level.FINEST);
List<FilterEntry<? extends WebSocket>> list = new ArrayList(webSocketFilter.getFilterEntrys());
@@ -334,56 +394,43 @@ public class NodeHttpServer extends NodeServer {
continue;
}
final Class<? extends WebSocket> stype = en.getType();
if (stype.getAnnotation(Rest.RestDyn.class) != null) continue;
RestWebSocket rs = stype.getAnnotation(RestWebSocket.class);
if (rs == null || rs.ignore()) return;
if (rs == null || rs.ignore()) continue;
final String stypename = stype.getName();
if (!autoload && !includeValues.contains(stypename)) return;
if (!restFilter.accept(stypename)) return;
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");
return;
continue;
}
restedObjects.add(stype); //避免重复创建Rest对象
WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
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 (ss != null) {
if (webss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix2 + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
synchronized (webss) {
webss.add(new AbstractMap.SimpleEntry<>(stype.getName() + "#" + rs.name(), mappings));
}
}
}
}
if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent);
//输出信息
if (ss != null && !ss.isEmpty() && sb != null) {
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
int max = 0;
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
if (as.getKey().length() > max) max = as.getKey().length();
}
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++) {
sb.append(' ');
}
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
}
sb.append(localThreadName).append(" All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR);
}
}
@Override //loadServlet执行之后调用
protected void postLoadServlets() {
final ClusterAgent cluster = application.clusterAgent;
if (cluster != null) {
if (!application.isCompileMode() && cluster != null) {
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (!cluster.containsProtocol(protocol)) return;

View File

@@ -94,7 +94,7 @@ public abstract class NodeServer {
//需要远程模式Service的MessageAgent对象集合
protected final Map<String, MessageAgent> sncpRemoteAgents = new HashMap<>();
private volatile int maxClassNameLength = 0;
private volatile int maxTypeLength = 0;
private volatile int maxNameLength = 0;
@@ -104,7 +104,11 @@ public abstract class NodeServer {
this.server = server;
this.resourceFactory = server.getResourceFactory();
this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
if (application.isCompileMode() || application.getServerClassLoader() instanceof RedkaleClassLoader.RedkaleCacheClassLoader) {
this.serverClassLoader = application.getServerClassLoader();
} else {
this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
}
Thread.currentThread().setContextClassLoader(this.serverClassLoader);
this.serverThread = Thread.currentThread();
this.server.setServerClassLoader(serverClassLoader);
@@ -112,7 +116,8 @@ public abstract class NodeServer {
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
try {
return clazz.getConstructor(Application.class, AnyValue.class).newInstance(application, serconf);
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName(), Application.class, AnyValue.class);
return clazz.getDeclaredConstructor(Application.class, AnyValue.class).newInstance(application, serconf);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -162,11 +167,12 @@ public abstract class NodeServer {
String interceptorClass = this.serverConf.getValue("interceptor", "");
if (!interceptorClass.isEmpty()) {
Class clazz = serverClassLoader.loadClass(interceptorClass);
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
this.interceptor = (NodeInterceptor) clazz.getDeclaredConstructor().newInstance();
}
ClassFilter<Service> serviceFilter = createServiceClassFilter();
if (application.singletonrun) { //singleton模式下只加载指定的Service
if (application.isSingletonMode()) { //singleton模式下只加载指定的Service
final String ssc = System.getProperty("red" + "kale.singleton.serviceclass");
final String extssc = System.getProperty("red" + "kale.singleton.extserviceclasses");
if (ssc != null) {
@@ -184,11 +190,11 @@ public abstract class NodeServer {
ClassFilter<Servlet> servletFilter = createServletClassFilter();
ClassFilter otherFilter = createOtherClassFilter();
long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), ((application.excludelibs != null ? (application.excludelibs + ";") : "") + serverConf.getValue("excludelibs", "")).split(";"), serviceFilter, filterFilter, servletFilter, otherFilter);
ClassFilter.Loader.load(application.getHome(), serverClassLoader, ((application.excludelibs != null ? (application.excludelibs + ";") : "") + serverConf.getValue("excludelibs", "")).split(";"), serviceFilter, filterFilter, servletFilter, otherFilter);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(serviceFilter, otherFilter); //必须在servlet之前
if (!application.singletonrun) { //非singleton模式下才加载FilterServlet
if (!application.isSingletonMode()) { //非singleton模式下才加载FilterServlet
loadFilter(filterFilter, otherFilter);
loadServlet(servletFilter, otherFilter);
postLoadServlets();
@@ -206,6 +212,7 @@ public abstract class NodeServer {
final ResourceFactory appResFactory = application.getResourceFactory();
final TransportFactory appSncpTranFactory = application.getSncpTransportFactory();
final AnyValue resources = application.config.getAnyValue("resources");
final String confURI = appResFactory.find(RESNAME_APP_CONF, String.class);
final Map<String, SimpleEntry<Class, AnyValue>> cacheResource = new HashMap<>();
final Map<String, SimpleEntry<Class, AnyValue>> dataResources = new HashMap<>();
if (resources != null) {
@@ -214,14 +221,25 @@ public abstract class NodeServer {
String classval = sourceConf.getValue("value");
Class type = null;
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class);
List<CacheSourceProvider> providers = new ArrayList<>();
Iterator<CacheSourceProvider> it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
type = s.getClass();
break;
CacheSourceProvider s = it.next();
if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName());
if (s != null && s.acceptsConf(sourceConf)) {
providers.add(s);
}
}
Collections.sort(providers, (a, b) -> {
Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class);
Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class);
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
});
for (CacheSourceProvider provider : providers) {
type = provider.sourceClass();
if (type != null) break;
}
} else {
type = serverClassLoader.loadClass(classval);
}
@@ -287,13 +305,13 @@ public abstract class NodeServer {
SncpClient client = src instanceof Service ? Sncp.getSncpClient((Service) src) : null;
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
Service service = Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null);
Service service = Modifier.isFinal(resServiceType.getModifiers()) ? (Service) resServiceType.getConstructor().newInstance() : Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null);
appResFactory.register(resourceName, resServiceType, service);
field.set(src, service);
rf.inject(service, self); // 给其可能包含@Resource的字段赋值;
service.init(null);
logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad) resourceName = " + resourceName + ", service = " + service);
if (!application.isCompileMode()) service.init(null);
logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad service = " + resServiceType.getSimpleName() + ", resourceName = '" + resourceName + "')");
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + src + " error", e);
}
@@ -313,6 +331,7 @@ public abstract class NodeServer {
source = DataSources.createDataSource(resourceName, sourceConf);
} else {
boolean can = false;
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
for (Constructor cr : sourceType.getConstructors()) {
if (cr.getParameterCount() == 0) {
can = true;
@@ -320,25 +339,38 @@ public abstract class NodeServer {
}
}
if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数
final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
if (Modifier.isFinal(sourceType.getModifiers()) || sourceType.getAnnotation(Local.class) != null) {
source = (DataSource) sourceType.getConstructor().newInstance();
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
} else {
final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
}
}
}
}
if (source == null) {
source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建
source = DataSources.createDataSource(confURI, resourceName); //从persistence.xml配置中创建
}
RedkaleClassLoader.putReflectionPublicConstructors(source.getClass(), source.getClass().getName());
application.dataSources.add(source);
ResourceType rt = source.getClass().getAnnotation(ResourceType.class);
if (rt != null && rt.value() != DataSource.class) {
appResFactory.register(resourceName, rt.value(), source);
} else if (source instanceof SearchSource) {
appResFactory.register(resourceName, SearchSource.class, source);
}
appResFactory.register(resourceName, DataSource.class, source);
field.set(src, source);
rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src);
if (source instanceof Service) ((Service) source).init(sourceConf);
if (!application.isCompileMode() && source instanceof Service) ((Service) source).init(sourceConf);
logger.info("[" + Thread.currentThread().getName() + "] Load DataSource (type = " + source.getClass().getSimpleName() + ", resourceName = '" + resourceName + "')");
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e);
}
@@ -365,14 +397,25 @@ public abstract class NodeServer {
if (sourceConf != null) {
String classval = sourceConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class);
List<CacheSourceProvider> providers = new ArrayList<>();
Iterator<CacheSourceProvider> it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
sourceType0 = s.getClass();
break;
CacheSourceProvider s = it.next();
if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName());
if (s != null && s.acceptsConf(sourceConf)) {
providers.add(s);
}
}
Collections.sort(providers, (a, b) -> {
Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class);
Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class);
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
});
for (CacheSourceProvider provider : providers) {
sourceType0 = provider.sourceClass();
if (sourceType0 != null) break;
}
} else {
sourceType0 = serverClassLoader.loadClass(classval);
}
@@ -380,7 +423,10 @@ public abstract class NodeServer {
final Class sourceType = sourceType0;
Object source = null;
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
source = Modifier.isFinal(sourceType.getModifiers()) ? sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
source = sourceType == CacheMemorySource.class ? new CacheMemorySource(resourceName)
: (Modifier.isFinal(sourceType.getModifiers()) || sourceType.getAnnotation(Local.class) != null) ? sourceType.getConstructor().newInstance()
: (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
application.cacheSources.add((CacheSource) source);
appResFactory.register(resourceName, CacheSource.class, source);
@@ -390,14 +436,16 @@ public abstract class NodeServer {
}
field.set(src, source);
rf.inject(source, self); //
if (source instanceof Service) ((Service) source).init(sourceConf);
if (!application.isCompileMode() && source instanceof Service) ((Service) source).init(sourceConf);
if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
sncpServer.getSncpServer().addSncpServlet((Service) source);
if (source != null && source.getClass().getAnnotation(Local.class) == null) { //本地模式的Service不生成SncpServlet
sncpServer.getSncpServer().addSncpServlet((Service) source);
}
//logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
}
logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + resourceName + ", source = " + source);
logger.info("[" + Thread.currentThread().getName() + "] Load CacheSource (type = " + source.getClass().getSimpleName() + ", resourceName = '" + resourceName + "')");
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e);
}
@@ -461,6 +509,7 @@ public abstract class NodeServer {
final Class<? extends Service> serviceImplClass = entry.getType();
if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过
if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue;
if (serviceImplClass.getAnnotation(SncpDyn.class) != null) continue; //动态生成的跳过
if (entry.isExpect()) {
if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
@@ -488,7 +537,7 @@ public abstract class NodeServer {
}
return;
}
RedkaleClassLoader.putReflectionPublicMethods(serviceImplClass.getName());
MessageAgent agent = null;
if (entry.getProperty() != null && entry.getProperty().getValue("mq") != null) {
agent = application.getMessageAgent(entry.getProperty().getValue("mq"));
@@ -543,7 +592,6 @@ public abstract class NodeServer {
//---------------- inject ----------------
new ArrayList<>(localServices).forEach(y -> {
resourceFactory.inject(y, NodeServer.this);
calcMaxLength(y);
});
new ArrayList<>(remoteServices).forEach(y -> {
resourceFactory.inject(y, NodeServer.this);
@@ -552,7 +600,7 @@ public abstract class NodeServer {
if (sb != null) {
remoteServices.forEach(y -> {
sb.append(localThreadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR);
sb.append(localThreadName).append(Sncp.toSimpleString(y, maxNameLength, maxTypeLength)).append(" load and inject").append(LINE_SEPARATOR);
});
}
if (isSNCP() && !sncpRemoteAgents.isEmpty()) {
@@ -563,6 +611,7 @@ public abstract class NodeServer {
}
//----------------- init -----------------
List<Service> swlist = new ArrayList<>(localServices);
swlist.forEach(y -> calcMaxLength(y));
swlist.sort((o1, o2) -> {
Priority p1 = o1.getClass().getAnnotation(Priority.class);
Priority p2 = o2.getClass().getAnnotation(Priority.class);
@@ -579,13 +628,20 @@ public abstract class NodeServer {
preInitServices(localServices, remoteServices);
long preinite = System.currentTimeMillis() - preinits;
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
localServices.stream().forEach(y -> {
long s = System.currentTimeMillis();
y.init(Sncp.getConf(y));
long e = System.currentTimeMillis() - s;
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength);
if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
});
if (application.isCompileMode()) {
localServices.stream().forEach(y -> {
String serstr = Sncp.toSimpleString(y, maxNameLength, maxTypeLength);
if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load").append(LINE_SEPARATOR).toString());
});
} else {
localServices.stream().forEach(y -> {
long s = System.currentTimeMillis();
y.init(Sncp.getConf(y));
long e = System.currentTimeMillis() - s;
String serstr = Sncp.toSimpleString(y, maxNameLength, maxTypeLength);
if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load and init in ").append(e < 10 ? " " : (e < 100 ? " " : "")).append(e).append(" ms").append(LINE_SEPARATOR).toString());
});
}
if (slist != null && sb != null) {
List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行)
for (String s : wlist) {
@@ -599,18 +655,19 @@ public abstract class NodeServer {
private void calcMaxLength(Service y) { //计算toString中的长度
maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length());
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
maxTypeLength = Math.max(maxTypeLength, Sncp.getResourceType(y).getName().length() + 1);
}
//Service.init执行之前调用
protected void preInitServices(Set<Service> localServices, Set<Service> remoteServices) {
final ClusterAgent cluster = application.clusterAgent;
if (cluster == null) return;
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, localServices, remoteServices);
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, localServices, remoteServices);
}
}
//loadServlet执行之后调用
@@ -620,7 +677,7 @@ public abstract class NodeServer {
//Service.destroy执行之前调用
protected void preDestroyServices(Set<Service> localServices, Set<Service> remoteServices) {
if (application.clusterAgent != null) { //服务注销
if (!application.isCompileMode() && application.clusterAgent != null) { //服务注销
final ClusterAgent cluster = application.clusterAgent;
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
@@ -629,7 +686,7 @@ public abstract class NodeServer {
afterClusterDeregisterOnPreDestroyServices(cluster, protocol);
}
}
if (!this.messageAgents.isEmpty()) { //MQ
if (!application.isCompileMode() && !this.messageAgents.isEmpty()) { //MQ
}
}
@@ -772,7 +829,7 @@ public abstract class NodeServer {
public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this);
server.start(application);
server.start();
postStartServer(localServices, remoteServices);
}
@@ -788,7 +845,7 @@ public abstract class NodeServer {
if (finest) logger.finest(y + " was destroyed");
long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) {
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);
sb.append(Sncp.toSimpleString(y, maxNameLength, maxTypeLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
@@ -825,7 +882,7 @@ public abstract class NodeServer {
}
long e = System.currentTimeMillis() - s;
if (e > 10 && sb != null) {
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" command (").append(cmd).append(") ").append(e).append("ms").append(LINE_SEPARATOR);
sb.append(Sncp.toSimpleString(y, maxNameLength, maxTypeLength)).append(" command (").append(cmd).append(") ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());

View File

@@ -33,8 +33,8 @@ public class NodeSncpServer extends NodeServer {
private NodeSncpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf));
this.sncpServer = (SncpServer) this.server;
this.consumer = sncpServer == null || application.singletonrun ? null : (agent, x) -> {//singleton模式下不生成SncpServlet
if (x.getClass().getAnnotation(Local.class) != null) return;
this.consumer = sncpServer == null || application.isSingletonMode() ? null : (agent, x) -> {//singleton模式下不生成SncpServlet
if (x.getClass().getAnnotation(Local.class) != null) return; //本地模式的Service不生成SncpServlet
SncpDynServlet servlet = sncpServer.addSncpServlet(x);
dynServletMap.put(x, servlet);
if (agent != null) agent.putService(this, x, servlet);
@@ -95,6 +95,7 @@ public class NodeSncpServer extends NodeServer {
for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
final SncpFilter filter = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
@@ -106,6 +107,8 @@ public class NodeSncpServer extends NodeServer {
@Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
RedkaleClassLoader.putReflectionPublicClasses(SncpServlet.class.getName());
RedkaleClassLoader.putReflectionPublicClasses(SncpDynServlet.class.getName());
}
@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 {
var w = param.required ? "font-weight:bold;" : "";
var c = ' style="' + w + '"';
if (param.src == "HEADER") c = ' style="color:red;' + w + '"';
if (param.src == "COOKIE") c = ' style="color:blue;' + w + '"';
if (param.style == "HEADER") c = ' style="color:red;' + 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>');
}
}

View File

@@ -80,7 +80,7 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
}
@Override //ServiceLoader时判断配置是否符合当前实现类
public boolean match(AnyValue config) {
public boolean acceptsConf(AnyValue config) {
if (config == null) return false;
AnyValue[] properties = config.getAnyValues("property");
if (properties == null || properties.length == 0) return false;

View File

@@ -85,7 +85,7 @@ public abstract class ClusterAgent {
}
//ServiceLoader时判断配置是否符合当前实现类
public abstract boolean match(AnyValue config);
public abstract boolean acceptsConf(AnyValue config);
public boolean containsProtocol(String protocol) {
if (protocol == null || protocol.isEmpty()) return false;

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

@@ -31,8 +31,14 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
out.writeClassName(null);
out.writeNull();
} else {
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(value.getClass()));
factory.loadEncoder(value.getClass()).convertTo(out, value);
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);
}
}

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

@@ -68,7 +68,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
out.writeNull();
return;
}
if (value.length == 0) {
int iMax = value.length - 1;
if (iMax == -1) {
out.writeArrayB(0, this, componentEncoder, value);
out.writeArrayE();
return;
@@ -87,28 +88,27 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
if (subtypefinal) {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, itemEncoder, v, first);
if (first) first = false;
for (int i = 0;; i++) {
writeMemberValue(out, member, itemEncoder, value[i], i);
if (i == iMax) break;
out.writeArrayMark();
}
}
} else {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
final Type comp = this.componentType;
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, first);
if (first) first = false;
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();
}
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);
}

View File

@@ -57,6 +57,10 @@ 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 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 Type type, final Object value);

View File

@@ -71,6 +71,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.parent = parent;
if (parent == null) {
//---------------------------------------------------------
this.register(boolean.class, BoolSimpledCoder.instance);
this.register(Boolean.class, BoolSimpledCoder.instance);
@@ -101,12 +102,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
this.register(StringBuilder.class, CharSequenceSimpledCoder.StringBuilderSimpledCoder.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(AtomicInteger.class, AtomicIntegerSimpledCoder.instance);
this.register(AtomicLong.class, AtomicLongSimpledCoder.instance);
this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
this.register(BigDecimal.class, BigDecimalSimpledCoder.instance);
this.register(InetAddress.class, InetAddressSimpledCoder.instance);
this.register(LongAdder.class, LongAdderSimpledCoder.instance);
this.register(DLong.class, DLongSimpledCoder.instance);
this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
@@ -141,48 +147,51 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
});
try {
Class sqldateClass = Class.forName("java.sql.Date");
this.register(sqldateClass, new SimpledCoder<R, W, java.sql.Date>() {
Class sqldateClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Date");
Invoker<Object, Object> sqldateInvoker = Invoker.create(sqldateClass, "valueOf", String.class);
this.register(sqldateClass, new SimpledCoder<R, W, Object>() {
@Override
public void convertTo(W out, java.sql.Date value) {
public void convertTo(W out, Object value) {
out.writeSmallString(value == null ? null : value.toString());
}
@Override
public java.sql.Date convertFrom(R in) {
public Object convertFrom(R in) {
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");
this.register(sqltimeClass, new SimpledCoder<R, W, java.sql.Time>() {
Class sqltimeClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Time");
Invoker<Object, Object> sqltimeInvoker = Invoker.create(sqltimeClass, "valueOf", String.class);
this.register(sqltimeClass, new SimpledCoder<R, W, Object>() {
@Override
public void convertTo(W out, java.sql.Time value) {
public void convertTo(W out, Object value) {
out.writeSmallString(value == null ? null : value.toString());
}
@Override
public java.sql.Time convertFrom(R in) {
public Object convertFrom(R in) {
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");
this.register(timestampClass, new SimpledCoder<R, W, java.sql.Timestamp>() {
Class timestampClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Timestamp");
Invoker<Object, Object> timestampInvoker = Invoker.create(timestampClass, "valueOf", String.class);
this.register(timestampClass, new SimpledCoder<R, W, Object>() {
@Override
public void convertTo(W out, java.sql.Timestamp value) {
public void convertTo(W out, Object value) {
out.writeSmallString(value == null ? null : value.toString());
}
@Override
public java.sql.Timestamp convertFrom(R in) {
public Object convertFrom(R in) {
String t = in.readSmallString();
return t == null ? null : java.sql.Timestamp.valueOf(t);
return t == null ? null : timestampInvoker.invoke(null, t);
}
});
@@ -204,9 +213,11 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
}
synchronized (loaderInited) {
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()) {
ConvertLoader cl = it.next();
ConvertProvider cl = it.next();
RedkaleClassLoader.putReflectionPublicConstructors(cl.getClass(), cl.getClass().getName());
if (cl.type() == ConvertType.PROTOBUF) defProtobufConvert = cl.convert();
}
loaderInited.set(true);
@@ -233,6 +244,24 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
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;
}
@@ -359,10 +388,20 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
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) {
if (method == null) return null;
String fname = method.getName();
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname;
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; //record类会直接用field名作为method名
fname = fname.substring(fname.startsWith("is") ? 2 : 3);
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
@@ -726,12 +765,15 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
try {
method.setAccessible(true);
simpleCoder = (Decodeable) method.invoke(null, this);
RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName());
RedkaleClassLoader.putReflectionMethod(clazz.getName(), method);
break;
} catch (Exception e) {
}
}
if (simpleCoder == null) {
od = createObjectDecoder(type);
Type impl = formatObjectType(type);
od = createObjectDecoder(impl);
decoder = od;
} else {
decoder = simpleCoder;
@@ -810,14 +852,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
try {
method.setAccessible(true);
simpleCoder = (Encodeable) method.invoke(null, this);
RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName());
RedkaleClassLoader.putReflectionMethod(clazz.getName(), method);
break;
} catch (Exception e) {
}
}
if (simpleCoder == null) {
encoder = createDyncEncoder(type);
Type impl = formatObjectType(type);
encoder = createDyncEncoder(impl);
if (encoder == null) {
oe = createObjectEncoder(type);
oe = createObjectEncoder(impl);
encoder = oe;
}
} else {

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
*
* @since 2.2.0
* @since 2.5.0
*/
public interface ConvertLoader {
public interface ConvertProvider {
public ConvertType type();

View File

@@ -22,6 +22,10 @@ import org.redkale.util.Attribute;
@SuppressWarnings("unchecked")
public final class DeMember<R extends Reader, T, F> {
final Field field; //对应类成员的Field 也可能为null
final Method method; //对应类成员的Method也可能为null
protected int index;
protected int position; //从1开始
@@ -32,33 +36,40 @@ public final class DeMember<R extends Reader, T, F> {
protected Decodeable<R, F> decoder;
public DeMember(final Attribute<T, F> attribute) {
public DeMember(final Attribute<T, F> attribute, Field field, Method method) {
this.attribute = attribute;
this.field = field;
this.method = method;
}
public DeMember(Attribute<T, F> attribute, Decodeable<R, F> decoder) {
this(attribute);
public DeMember(Attribute<T, F> attribute, Decodeable<R, F> decoder, Field field, Method method) {
this(attribute, field, method);
this.decoder = decoder;
}
public static <R extends Reader, T, F> DeMember<R, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldname) {
try {
Field field = clazz.getDeclaredField(fieldname);
return new DeMember<>(Attribute.create(field), factory.loadDecoder(field.getGenericType()));
return new DeMember<>(Attribute.create(field), factory.loadDecoder(field.getGenericType()), field, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <R extends Reader, T, F> DeMember<R, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldname, final Class<F> fieldtype) {
return new DeMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadDecoder(fieldtype));
try {
Field field = clazz.getDeclaredField(fieldname);
return new DeMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadDecoder(fieldtype), field, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <R extends Reader, T, F> DeMember<R, T, F> create(final Attribute<T, F> attribute, final ConvertFactory factory, final Class<F> fieldtype) {
return new DeMember<>(attribute, factory.loadDecoder(fieldtype));
return new DeMember<>(attribute, factory.loadDecoder(fieldtype), null, null);
}
public final boolean match(String name) {
public final boolean accepts(String name) {
return attribute.field().equals(name);
}
@@ -78,6 +89,14 @@ public final class DeMember<R extends Reader, T, F> {
return decoder;
}
public Field getField() {
return field;
}
public Method getMethod() {
return method;
}
public int getIndex() {
return this.index;
}

View File

@@ -35,15 +35,21 @@ public final class EnMember<W extends Writer, T, F> {
final byte[] jsonFieldNameBytes;
final Field field; //对应类成员的Field也可能为null
final Method method; //对应类成员的Method也可能为null
protected int index;
protected int position; //从1开始
protected int tag; //主要给protobuf使用
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) {
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder, Field field, Method method) {
this.attribute = attribute;
this.encoder = encoder;
this.field = field;
this.method = method;
Class t = attribute.type();
this.string = CharSequence.class.isAssignableFrom(t);
this.bool = t == Boolean.class || t == boolean.class;
@@ -55,21 +61,26 @@ public final class EnMember<W extends Writer, T, F> {
public static <W extends Writer, T, F> EnMember<W, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldname) {
try {
Field field = clazz.getDeclaredField(fieldname);
return new EnMember<>(Attribute.create(field), factory.loadEncoder(field.getGenericType()));
return new EnMember<>(Attribute.create(field), factory.loadEncoder(field.getGenericType()), field, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <W extends Writer, T, F> EnMember<W, T, F> create(final ConvertFactory factory, final Class<T> clazz, final String fieldname, final Class<F> fieldtype) {
return new EnMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadEncoder(fieldtype));
try {
Field field = clazz.getDeclaredField(fieldname);
return new EnMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadEncoder(fieldtype), field, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <W extends Writer, T, F> EnMember<W, T, F> create(final Attribute<T, F> attribute, final ConvertFactory factory, final Class<F> fieldtype) {
return new EnMember<>(attribute, factory.loadEncoder(fieldtype));
return new EnMember<>(attribute, factory.loadEncoder(fieldtype), null, null);
}
public final boolean match(String name) {
public final boolean accepts(String name) {
return attribute.field().equals(name);
}
@@ -93,6 +104,14 @@ public final class EnMember<W extends Writer, T, F> {
return string;
}
public Field getField() {
return field;
}
public Method getMethod() {
return method;
}
public boolean isBoolType() {
return bool;
}

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