Compare commits
237 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad567b4c40 | ||
|
|
6f8aeea870 | ||
|
|
87a1a7c641 | ||
|
|
178f96a3b5 | ||
|
|
cc629a1a7c | ||
|
|
1cb72df52e | ||
|
|
4c32422493 | ||
|
|
7361823ece | ||
|
|
03824a900c | ||
|
|
c40913d690 | ||
|
|
bdfdf153f7 | ||
|
|
fa9e62d2a7 | ||
|
|
bcca20dbbb | ||
|
|
bca42ee7b4 | ||
|
|
d5f2bae47c | ||
|
|
fe1e99c67c | ||
|
|
699d55d70e | ||
|
|
3d11a95a29 | ||
|
|
a983c53189 | ||
|
|
0ee28f59ce | ||
|
|
96ee0584b8 | ||
|
|
7352d5b8ed | ||
|
|
e79fdd887c | ||
|
|
7b5b0f34ff | ||
|
|
ae8fb992fb | ||
|
|
d5409518ed | ||
|
|
6bb2e152a5 | ||
|
|
c73ebf88db | ||
|
|
bfdceec81b | ||
|
|
dd444e3a0f | ||
|
|
79de8dbbca | ||
|
|
5c6f3c3712 | ||
|
|
0d0663a7de | ||
|
|
6d1c4e6c85 | ||
|
|
f539633497 | ||
|
|
d9ddd6d772 | ||
|
|
4a2ba4e914 | ||
|
|
cc864e3e69 | ||
|
|
4cd5bd37d3 | ||
|
|
ff14e675fa | ||
|
|
a46cb462a2 | ||
|
|
8077dedd03 | ||
|
|
6a707dff0c | ||
|
|
c9aa31d803 | ||
|
|
4494683db2 | ||
|
|
f440b2d639 | ||
|
|
324d4bd94e | ||
|
|
bb82d70f5a | ||
|
|
8d1022f70f | ||
|
|
00034981ef | ||
|
|
2b3b33979c | ||
|
|
8e0788ccf3 | ||
|
|
6e81541a3b | ||
|
|
6acb17da7c | ||
|
|
774286952b | ||
|
|
35a3c81391 | ||
|
|
90d28eb4f4 | ||
|
|
aab24fdbbe | ||
|
|
d0c24ed7a7 | ||
|
|
2260906396 | ||
|
|
0901195147 | ||
|
|
c11a8e7bd9 | ||
|
|
8176ec1b0a | ||
|
|
8f598fe4eb | ||
|
|
2fac5c853e | ||
|
|
4dbd6b57f8 | ||
|
|
a5f21a99d8 | ||
|
|
6f98b44471 | ||
|
|
b13b07fb05 | ||
|
|
1f8884a415 | ||
|
|
848a30b2db | ||
|
|
350ece1533 | ||
|
|
513183f55c | ||
|
|
b23434e3bb | ||
|
|
7084fb2ea2 | ||
|
|
5becdf4d50 | ||
|
|
5aa10e9d74 | ||
|
|
db462ec134 | ||
|
|
75e9f6162b | ||
|
|
970d530fbd | ||
|
|
13ffa2a3e5 | ||
|
|
8416826827 | ||
|
|
0a296ee857 | ||
|
|
bb45470078 | ||
|
|
d3c6ab8dc5 | ||
|
|
0bf34781c5 | ||
|
|
20940e82de | ||
|
|
78df572bec | ||
|
|
ffd0bec5c6 | ||
|
|
62783d0765 | ||
|
|
7116f33a3a | ||
|
|
a168dab8c0 | ||
|
|
e3e065f36e | ||
|
|
d282321d98 | ||
|
|
1abdb3b874 | ||
|
|
1807287321 | ||
|
|
a29652180b | ||
|
|
d4a85ac136 | ||
|
|
007f635081 | ||
|
|
f75e15a375 | ||
|
|
508c7c4c0a | ||
|
|
7166dc0ec0 | ||
|
|
1f28b7e803 | ||
|
|
5d77139965 | ||
|
|
126dab08b2 | ||
|
|
a8175d9e19 | ||
|
|
a5c11b5119 | ||
|
|
bfc6b04a30 | ||
|
|
a5dd878c66 | ||
|
|
ed7ce80565 | ||
|
|
e6b71a3975 | ||
|
|
ff5049edc9 | ||
|
|
a34218367b | ||
|
|
f712107fb8 | ||
|
|
fb3dd6049d | ||
|
|
13bd467152 | ||
|
|
d934f615ca | ||
|
|
5e5280a7fd | ||
|
|
be2a4d252f | ||
|
|
0392fd68a8 | ||
|
|
e642854e99 | ||
|
|
1a42292725 | ||
|
|
c523a761c5 | ||
|
|
a416ae564a | ||
|
|
ecd647ecc4 | ||
|
|
50d0096492 | ||
|
|
1a2d43479f | ||
|
|
2416e93004 | ||
|
|
db99445878 | ||
|
|
18459b71c2 | ||
|
|
16cbabc79d | ||
|
|
c470d31605 | ||
|
|
cdacc30633 | ||
|
|
e55d4d8a78 | ||
|
|
fcd3258a73 | ||
|
|
a8e9822381 | ||
|
|
dfdb37973f | ||
|
|
3f340a3f7e | ||
|
|
e64d522707 | ||
|
|
6c05a4a038 | ||
|
|
5c9279cca4 | ||
|
|
a4ae6dd9c4 | ||
|
|
ace4042649 | ||
|
|
76debd5c82 | ||
|
|
8c0db6db53 | ||
|
|
bc99f3f295 | ||
|
|
826aaf0128 | ||
|
|
754861e036 | ||
|
|
b1bbc50472 | ||
|
|
dcb6c6d3f8 | ||
|
|
3f608b91e9 | ||
|
|
9d488974a4 | ||
|
|
1fa834f559 | ||
|
|
812f83446e | ||
|
|
3497ab0c73 | ||
|
|
78de131f83 | ||
|
|
3a8ffa11fe | ||
|
|
dd62d82005 | ||
|
|
7b9c71dcda | ||
|
|
4a518f1309 | ||
|
|
3a0a717765 | ||
|
|
60bdf4d137 | ||
|
|
0ec329927a | ||
|
|
6a616b4652 | ||
|
|
0bf7ccd701 | ||
|
|
c924d936f3 | ||
|
|
ef1c437191 | ||
|
|
6e6d0529d6 | ||
|
|
2ab179568f | ||
|
|
dab56caa98 | ||
|
|
67afd13fbb | ||
|
|
3a26ddd539 | ||
|
|
5197d63c70 | ||
|
|
3d136b843e | ||
|
|
388b7fbba7 | ||
|
|
a833f20729 | ||
|
|
d3c63bf35a | ||
|
|
5a0eff2b67 | ||
|
|
e64a75cfab | ||
|
|
2d31b8521d | ||
|
|
60225e3f5f | ||
|
|
aeb7b38474 | ||
|
|
bfbcab4009 | ||
|
|
742c352080 | ||
|
|
643827354c | ||
|
|
2f7bebfc17 | ||
|
|
7bf8d60ddf | ||
|
|
09c51b6b4e | ||
|
|
84f6ce58a0 | ||
|
|
9e90ae5285 | ||
|
|
93b1c9f0d9 | ||
|
|
dab8ed8ceb | ||
|
|
f016e49111 | ||
|
|
b403f22284 | ||
|
|
ccb9ac93f7 | ||
|
|
72935f1ebe | ||
|
|
1f7d46219a | ||
|
|
9094f76de2 | ||
|
|
d67cbeb3f2 | ||
|
|
90562ebd04 | ||
|
|
b14e14659c | ||
|
|
84a15afc9a | ||
|
|
8c1aba5608 | ||
|
|
a72c689f07 | ||
|
|
1d74f34575 | ||
|
|
a24092d391 | ||
|
|
097ae701c1 | ||
|
|
e250a593a7 | ||
|
|
1547e6b714 | ||
|
|
9f14224269 | ||
|
|
ecc54f9bd5 | ||
|
|
521ee56d31 | ||
|
|
f9da063532 | ||
|
|
5070c27f6a | ||
|
|
c0664bb0a9 | ||
|
|
f8c92f1ec4 | ||
|
|
01930cfdc8 | ||
|
|
e76e321765 | ||
|
|
99d2db31f7 | ||
|
|
fa9015447b | ||
|
|
735ad0908b | ||
|
|
a080a6a8cc | ||
|
|
2cbc51cfdf | ||
|
|
8631b4bdf5 | ||
|
|
285cb86891 | ||
|
|
bf535a7161 | ||
|
|
34e37471b8 | ||
|
|
e223548b23 | ||
|
|
3a13d242f6 | ||
|
|
204e6ec99f | ||
|
|
7e348782e4 | ||
|
|
a4cbe7db17 | ||
|
|
f8101acb4b | ||
|
|
9ed2d59317 | ||
|
|
0329ad7832 | ||
|
|
53df45456f | ||
|
|
93698bacff |
@@ -4,4 +4,4 @@ SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DCMD=APIDOC -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* org.redkale.boot.Application
|
||||
java -DCMD=APIDOC -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
|
||||
@@ -4,6 +4,6 @@ SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
call %APP_HOME%\bin\shutdown.bat
|
||||
call "%APP_HOME%\bin\shutdown.bat"
|
||||
|
||||
call %APP_HOME%\bin\start.bat
|
||||
call "%APP_HOME%\bin\start.bat"
|
||||
@@ -4,4 +4,4 @@ SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DCMD=SHUTDOWN -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* org.redkale.boot.Application
|
||||
java -DCMD=SHUTDOWN -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
|
||||
@@ -4,5 +4,5 @@ SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* org.redkale.boot.Application
|
||||
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?autoReconnect=true&characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
threads: 线程总数, 默认: <group>节点数*CPU核数*2
|
||||
bufferCapacity: ByteBuffer的初始化大小, 默认: 32K;
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程总数*4
|
||||
readTimeoutSecond: TCP读取超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
writeTimeoutSecond: TCP写入超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
|
||||
-->
|
||||
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSecond="6" writeTimeoutSecond="6"/>
|
||||
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
|
||||
|
||||
<!--
|
||||
一个组包含多个node, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
||||
@@ -85,7 +85,9 @@
|
||||
如果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.json.tiny", "true");
|
||||
System.setProperty("convert.bson.tiny", "true");
|
||||
System.setProperty("convert.json.pool.size", "128");
|
||||
@@ -114,18 +116,31 @@
|
||||
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
|
||||
charset: 文本编码, 默认: UTF-8
|
||||
backlog: 默认10K
|
||||
threads: 线程总数, 默认: CPU核数*8
|
||||
threads: 线程数, 默认: CPU核数*32
|
||||
maxconns:最大连接数, 小于1表示无限制, 默认: 0
|
||||
maxbody: request.body最大值, 默认: 64K
|
||||
bufferCapacity: ByteBuffer的初始化大小, 默认: 32K; (HTTP 2.0、WebSocket,必须要16k以上)
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程总数*4
|
||||
responsePoolSize: Response池的大小,默认: 线程总数*2
|
||||
readTimeoutSecond: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSecond: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程数*4
|
||||
responsePoolSize: Response池的大小,默认: 线程数*2
|
||||
aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30, 0表示永久不超时; -1表示禁止KeepAlive
|
||||
readTimeoutSeconds: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSeconds: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
netimpl: ProtocolServer的实现类。TCP情况下值也可以是aio或nio,默认值为aio;UDP情况下值也可以是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文件
|
||||
-->
|
||||
<ssl creator=""/>
|
||||
|
||||
<!--
|
||||
加载所有的Service服务;
|
||||
在同一个进程中同一个name同一类型的Service将共用同一个实例
|
||||
@@ -216,26 +231,33 @@
|
||||
<!--
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时, response节点才有效。
|
||||
contenttype: plain值为调用finish时的ContentType; 默认值: text/plain; charset=utf-8
|
||||
json值为调用finishJson时的ContentType; 默认值: application/json; charset=utf-8
|
||||
defcookie 节点: 当response里输出的cookie没有指定domain 和path时,使用该节点的默认值。
|
||||
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
|
||||
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
|
||||
例如下面例子是在Response输出header时添加两个header(一个addHeader, 一个setHeader)。
|
||||
options 节点: 设置了该节点却auto=true,当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
|
||||
options 节点: 设置了该节点且auto=true,当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
|
||||
date 节点: 设置了该节点且period有值(单位:毫秒);返回response会包含Date头信息,默认为period=0
|
||||
period=0表示实时获取当前时间;
|
||||
period<0表示不设置date;
|
||||
period>0表示定时获取时间; 设置1000表示每秒刷新Date时间
|
||||
-->
|
||||
<response>
|
||||
<contenttype 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"/>
|
||||
<setheader name="Access-Control-Allow-Credentials" value="true"/>
|
||||
<options auto="true" />
|
||||
<date period="0" />
|
||||
</response>
|
||||
<!--
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时,render才有效. 指定输出引擎的实现类
|
||||
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
|
||||
-->
|
||||
<render value="org.redkalex.htel.HttpTemplateRender">
|
||||
</render>
|
||||
<render value="org.redkalex.htel.HttpTemplateRender"/>
|
||||
<!--
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时,ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
|
||||
|
||||
@@ -70,7 +70,7 @@ public abstract class AnnotationVisitor {
|
||||
|
||||
/**
|
||||
* The ASM API version implemented by this visitor. The value of this field
|
||||
* must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
protected final int api;
|
||||
|
||||
@@ -85,7 +85,7 @@ public abstract class AnnotationVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
public AnnotationVisitor(final int api) {
|
||||
this(api, null);
|
||||
@@ -96,15 +96,12 @@ public abstract class AnnotationVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
* @param av
|
||||
* the annotation visitor to which this visitor must delegate
|
||||
* method calls. May be null.
|
||||
*/
|
||||
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
|
||||
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.api = api;
|
||||
this.av = av;
|
||||
}
|
||||
@@ -118,7 +115,7 @@ public abstract class AnnotationVisitor {
|
||||
* the actual value, whose type must be {@link Byte},
|
||||
* {@link Boolean}, {@link Character}, {@link Short},
|
||||
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
|
||||
* {@link String} or {@link Type} or OBJECT or ARRAY sort. This
|
||||
* {@link String} or {@link Type} of OBJECT or ARRAY sort. This
|
||||
* value can also be an array of byte, boolean, short, char, int,
|
||||
* long, float or double values (this is equivalent to using
|
||||
* {@link #visitArray visitArray} and visiting each array element
|
||||
|
||||
@@ -133,7 +133,7 @@ final class AnnotationWriter extends AnnotationVisitor {
|
||||
*/
|
||||
AnnotationWriter(final ClassWriter cw, final boolean named,
|
||||
final ByteVector bv, final ByteVector parent, final int offset) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM6);
|
||||
this.cw = cw;
|
||||
this.named = named;
|
||||
this.bv = bv;
|
||||
|
||||
@@ -64,7 +64,7 @@ package org.redkale.asm;
|
||||
* @author Eric Bruneton
|
||||
* @author Eugene Kuleshov
|
||||
*/
|
||||
public class Attribute {
|
||||
class Attribute {
|
||||
|
||||
/**
|
||||
* The type of this attribute.
|
||||
|
||||
@@ -72,31 +72,6 @@ import java.io.InputStream;
|
||||
*/
|
||||
public class ClassReader {
|
||||
|
||||
/**
|
||||
* True to enable signatures support.
|
||||
*/
|
||||
static final boolean SIGNATURES = true;
|
||||
|
||||
/**
|
||||
* True to enable annotations support.
|
||||
*/
|
||||
static final boolean ANNOTATIONS = true;
|
||||
|
||||
/**
|
||||
* True to enable stack map frames support.
|
||||
*/
|
||||
static final boolean FRAMES = true;
|
||||
|
||||
/**
|
||||
* True to enable bytecode writing support.
|
||||
*/
|
||||
static final boolean WRITER = true;
|
||||
|
||||
/**
|
||||
* True to enable JSR_W and GOTO_W support.
|
||||
*/
|
||||
static final boolean RESIZE = true;
|
||||
|
||||
/**
|
||||
* Flag to skip method code. If this class is set <code>CODE</code>
|
||||
* attribute won't be visited. This can be used, for example, to retrieve
|
||||
@@ -133,6 +108,21 @@ public class ClassReader {
|
||||
*/
|
||||
public static final int EXPAND_FRAMES = 8;
|
||||
|
||||
/**
|
||||
* Flag to expand the ASM pseudo instructions into an equivalent sequence of
|
||||
* standard bytecode instructions. When resolving a forward jump it may
|
||||
* happen that the signed 2 bytes offset reserved for it is not sufficient
|
||||
* to store the bytecode offset. In this case the jump instruction is
|
||||
* replaced with a temporary ASM pseudo instruction using an unsigned 2
|
||||
* bytes offset (see Label#resolve). This internal flag is used to re-read
|
||||
* classes containing such instructions, in order to replace them with
|
||||
* standard instructions. In addition, when this flag is used, GOTO_W and
|
||||
* JSR_W are <i>not</i> converted into GOTO and JSR, to make sure that
|
||||
* infinite loops where a GOTO_W is replaced with a GOTO in ClassReader and
|
||||
* converted back to a GOTO_W in ClassWriter cannot occur.
|
||||
*/
|
||||
static final int EXPAND_ASM_INSNS = 256;
|
||||
|
||||
/**
|
||||
* The class to be parsed. <i>The content of this array must not be
|
||||
* modified. This field is intended for {@link Attribute} sub classes, and
|
||||
@@ -195,8 +185,8 @@ public class ClassReader {
|
||||
public ClassReader(final byte[] b, final int off, final int len) {
|
||||
this.b = b;
|
||||
// checks the class version
|
||||
if (readShort(off + 6) > Opcodes.V1_9) {
|
||||
throw new IllegalArgumentException();
|
||||
if (readShort(off + 6) > Opcodes.V10) {
|
||||
//throw new IllegalArgumentException();
|
||||
}
|
||||
// parses the constant pool
|
||||
items = new int[readUnsignedShort(off + 8)];
|
||||
@@ -234,6 +224,8 @@ public class ClassReader {
|
||||
// case ClassWriter.CLASS:
|
||||
// case ClassWriter.STR:
|
||||
// case ClassWriter.MTYPE
|
||||
// case ClassWriter.PACKAGE:
|
||||
// case ClassWriter.MODULE:
|
||||
default:
|
||||
size = 3;
|
||||
break;
|
||||
@@ -377,7 +369,9 @@ public class ClassReader {
|
||||
break;
|
||||
// case ClassWriter.STR:
|
||||
// case ClassWriter.CLASS:
|
||||
// case ClassWriter.MTYPE
|
||||
// case ClassWriter.MTYPE:
|
||||
// case ClassWriter.MODULE:
|
||||
// case ClassWriter.PACKAGE:
|
||||
default:
|
||||
item.set(tag, readUTF8(index, buf), null, null);
|
||||
break;
|
||||
@@ -584,11 +578,14 @@ public class ClassReader {
|
||||
String enclosingOwner = null;
|
||||
String enclosingName = null;
|
||||
String enclosingDesc = null;
|
||||
String moduleMainClass = null;
|
||||
int anns = 0;
|
||||
int ianns = 0;
|
||||
int tanns = 0;
|
||||
int itanns = 0;
|
||||
int innerClasses = 0;
|
||||
int module = 0;
|
||||
int packages = 0;
|
||||
Attribute attributes = null;
|
||||
|
||||
u = getAttributes();
|
||||
@@ -607,13 +604,11 @@ public class ClassReader {
|
||||
enclosingName = readUTF8(items[item], c);
|
||||
enclosingDesc = readUTF8(items[item] + 2, c);
|
||||
}
|
||||
} else if (SIGNATURES && "Signature".equals(attrName)) {
|
||||
} else if ("Signature".equals(attrName)) {
|
||||
signature = readUTF8(u + 8, c);
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleAnnotations".equals(attrName)) {
|
||||
anns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
tanns = u + 8;
|
||||
} else if ("Deprecated".equals(attrName)) {
|
||||
access |= Opcodes.ACC_DEPRECATED;
|
||||
@@ -623,12 +618,16 @@ public class ClassReader {
|
||||
} else if ("SourceDebugExtension".equals(attrName)) {
|
||||
int len = readInt(u + 4);
|
||||
sourceDebug = readUTF(u + 8, len, new char[len]);
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
|
||||
ianns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
itanns = u + 8;
|
||||
} else if ("Module".equals(attrName)) {
|
||||
module = u + 8;
|
||||
} else if ("ModuleMainClass".equals(attrName)) {
|
||||
moduleMainClass = readClass(u + 8, c);
|
||||
} else if ("ModulePackages".equals(attrName)) {
|
||||
packages = u + 10;
|
||||
} else if ("BootstrapMethods".equals(attrName)) {
|
||||
int[] bootstrapMethods = new int[readUnsignedShort(u + 8)];
|
||||
for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) {
|
||||
@@ -657,6 +656,12 @@ public class ClassReader {
|
||||
classVisitor.visitSource(sourceFile, sourceDebug);
|
||||
}
|
||||
|
||||
// visits the module info and associated attributes
|
||||
if (module != 0) {
|
||||
readModule(classVisitor, context, module,
|
||||
moduleMainClass, packages);
|
||||
}
|
||||
|
||||
// visits the outer class
|
||||
if (enclosingOwner != null) {
|
||||
classVisitor.visitOuterClass(enclosingOwner, enclosingName,
|
||||
@@ -664,19 +669,19 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
// visits the class annotations and type annotations
|
||||
if (ANNOTATIONS && anns != 0) {
|
||||
if (anns != 0) {
|
||||
for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
classVisitor.visitAnnotation(readUTF8(v, c), true));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && ianns != 0) {
|
||||
if (ianns != 0) {
|
||||
for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
classVisitor.visitAnnotation(readUTF8(v, c), false));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && tanns != 0) {
|
||||
if (tanns != 0) {
|
||||
for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
|
||||
v = readAnnotationTarget(context, v);
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
@@ -684,7 +689,7 @@ public class ClassReader {
|
||||
context.typePath, readUTF8(v, c), true));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && itanns != 0) {
|
||||
if (itanns != 0) {
|
||||
for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
|
||||
v = readAnnotationTarget(context, v);
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
@@ -726,6 +731,120 @@ public class ClassReader {
|
||||
classVisitor.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the module attribute and visit it.
|
||||
*
|
||||
* @param classVisitor
|
||||
* the current class visitor
|
||||
* @param context
|
||||
* information about the class being parsed.
|
||||
* @param u
|
||||
* start offset of the module attribute in the class file.
|
||||
* @param mainClass
|
||||
* name of the main class of a module or null.
|
||||
* @param packages
|
||||
* start offset of the concealed package attribute.
|
||||
*/
|
||||
private void readModule(final ClassVisitor classVisitor,
|
||||
final Context context, int u,
|
||||
final String mainClass, int packages) {
|
||||
|
||||
char[] buffer = context.buffer;
|
||||
|
||||
// reads module name, flags and version
|
||||
String name = readModule(u, buffer);
|
||||
int flags = readUnsignedShort(u + 2);
|
||||
String version = readUTF8(u + 4, buffer);
|
||||
u += 6;
|
||||
|
||||
ModuleVisitor mv = classVisitor.visitModule(name, flags, version);
|
||||
if (mv == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// module attributes (main class, packages)
|
||||
if (mainClass != null) {
|
||||
mv.visitMainClass(mainClass);
|
||||
}
|
||||
|
||||
if (packages != 0) {
|
||||
for (int i = readUnsignedShort(packages - 2); i > 0; --i) {
|
||||
String packaze = readPackage(packages, buffer);
|
||||
mv.visitPackage(packaze);
|
||||
packages += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// reads requires
|
||||
u += 2;
|
||||
for (int i = readUnsignedShort(u - 2); i > 0; --i) {
|
||||
String module = readModule(u, buffer);
|
||||
int access = readUnsignedShort(u + 2);
|
||||
String requireVersion = readUTF8(u + 4, buffer);
|
||||
mv.visitRequire(module, access, requireVersion);
|
||||
u += 6;
|
||||
}
|
||||
|
||||
// reads exports
|
||||
u += 2;
|
||||
for (int i = readUnsignedShort(u - 2); i > 0; --i) {
|
||||
String export = readPackage(u, buffer);
|
||||
int access = readUnsignedShort(u + 2);
|
||||
int exportToCount = readUnsignedShort(u + 4);
|
||||
u += 6;
|
||||
String[] tos = null;
|
||||
if (exportToCount != 0) {
|
||||
tos = new String[exportToCount];
|
||||
for (int j = 0; j < tos.length; ++j) {
|
||||
tos[j] = readModule(u, buffer);
|
||||
u += 2;
|
||||
}
|
||||
}
|
||||
mv.visitExport(export, access, tos);
|
||||
}
|
||||
|
||||
// reads opens
|
||||
u += 2;
|
||||
for (int i = readUnsignedShort(u - 2); i > 0; --i) {
|
||||
String open = readPackage(u, buffer);
|
||||
int access = readUnsignedShort(u + 2);
|
||||
int openToCount = readUnsignedShort(u + 4);
|
||||
u += 6;
|
||||
String[] tos = null;
|
||||
if (openToCount != 0) {
|
||||
tos = new String[openToCount];
|
||||
for (int j = 0; j < tos.length; ++j) {
|
||||
tos[j] = readModule(u, buffer);
|
||||
u += 2;
|
||||
}
|
||||
}
|
||||
mv.visitOpen(open, access, tos);
|
||||
}
|
||||
|
||||
// read uses
|
||||
u += 2;
|
||||
for (int i = readUnsignedShort(u - 2); i > 0; --i) {
|
||||
mv.visitUse(readClass(u, buffer));
|
||||
u += 2;
|
||||
}
|
||||
|
||||
// read provides
|
||||
u += 2;
|
||||
for (int i = readUnsignedShort(u - 2); i > 0; --i) {
|
||||
String service = readClass(u, buffer);
|
||||
int provideWithCount = readUnsignedShort(u + 2);
|
||||
u += 4;
|
||||
String[] withs = new String[provideWithCount];
|
||||
for (int j = 0; j < withs.length; ++j) {
|
||||
withs[j] = readClass(u, buffer);
|
||||
u += 2;
|
||||
}
|
||||
mv.visitProvide(service, withs);
|
||||
}
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a field and makes the given visitor visit it.
|
||||
*
|
||||
@@ -762,24 +881,20 @@ public class ClassReader {
|
||||
if ("ConstantValue".equals(attrName)) {
|
||||
int item = readUnsignedShort(u + 8);
|
||||
value = item == 0 ? null : readConst(item, c);
|
||||
} else if (SIGNATURES && "Signature".equals(attrName)) {
|
||||
} else if ("Signature".equals(attrName)) {
|
||||
signature = readUTF8(u + 8, c);
|
||||
} else if ("Deprecated".equals(attrName)) {
|
||||
access |= Opcodes.ACC_DEPRECATED;
|
||||
} else if ("Synthetic".equals(attrName)) {
|
||||
access |= Opcodes.ACC_SYNTHETIC
|
||||
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleAnnotations".equals(attrName)) {
|
||||
anns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
tanns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
|
||||
ianns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
itanns = u + 8;
|
||||
} else {
|
||||
Attribute attr = readAttribute(context.attrs, attrName, u + 8,
|
||||
@@ -801,19 +916,19 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
// visits the field annotations and type annotations
|
||||
if (ANNOTATIONS && anns != 0) {
|
||||
if (anns != 0) {
|
||||
for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
fv.visitAnnotation(readUTF8(v, c), true));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && ianns != 0) {
|
||||
if (ianns != 0) {
|
||||
for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
fv.visitAnnotation(readUTF8(v, c), false));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && tanns != 0) {
|
||||
if (tanns != 0) {
|
||||
for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
|
||||
v = readAnnotationTarget(context, v);
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
@@ -821,7 +936,7 @@ public class ClassReader {
|
||||
context.typePath, readUTF8(v, c), true));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && itanns != 0) {
|
||||
if (itanns != 0) {
|
||||
for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
|
||||
v = readAnnotationTarget(context, v);
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
@@ -895,32 +1010,26 @@ public class ClassReader {
|
||||
exceptions[j] = readClass(exception, c);
|
||||
exception += 2;
|
||||
}
|
||||
} else if (SIGNATURES && "Signature".equals(attrName)) {
|
||||
} else if ("Signature".equals(attrName)) {
|
||||
signature = readUTF8(u + 8, c);
|
||||
} else if ("Deprecated".equals(attrName)) {
|
||||
context.access |= Opcodes.ACC_DEPRECATED;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleAnnotations".equals(attrName)) {
|
||||
anns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
tanns = u + 8;
|
||||
} else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) {
|
||||
} else if ("AnnotationDefault".equals(attrName)) {
|
||||
dann = u + 8;
|
||||
} else if ("Synthetic".equals(attrName)) {
|
||||
context.access |= Opcodes.ACC_SYNTHETIC
|
||||
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
|
||||
ianns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
itanns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleParameterAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) {
|
||||
mpanns = u + 8;
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleParameterAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) {
|
||||
impanns = u + 8;
|
||||
} else if ("MethodParameters".equals(attrName)) {
|
||||
methodParameters = u + 8;
|
||||
@@ -953,7 +1062,7 @@ public class ClassReader {
|
||||
* access, name and descriptor can have been changed, this is not
|
||||
* important since they are not copied as is from the reader).
|
||||
*/
|
||||
if (WRITER && mv instanceof MethodWriter) {
|
||||
if (mv instanceof MethodWriter) {
|
||||
MethodWriter mw = (MethodWriter) mv;
|
||||
if (mw.cw.cr == this && signature == mw.signature) {
|
||||
boolean sameExceptions = false;
|
||||
@@ -990,26 +1099,26 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
// visits the method annotations
|
||||
if (ANNOTATIONS && dann != 0) {
|
||||
if (dann != 0) {
|
||||
AnnotationVisitor dv = mv.visitAnnotationDefault();
|
||||
readAnnotationValue(dann, c, null, dv);
|
||||
if (dv != null) {
|
||||
dv.visitEnd();
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && anns != 0) {
|
||||
if (anns != 0) {
|
||||
for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
mv.visitAnnotation(readUTF8(v, c), true));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && ianns != 0) {
|
||||
if (ianns != 0) {
|
||||
for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
mv.visitAnnotation(readUTF8(v, c), false));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && tanns != 0) {
|
||||
if (tanns != 0) {
|
||||
for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
|
||||
v = readAnnotationTarget(context, v);
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
@@ -1017,7 +1126,7 @@ public class ClassReader {
|
||||
context.typePath, readUTF8(v, c), true));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && itanns != 0) {
|
||||
if (itanns != 0) {
|
||||
for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
|
||||
v = readAnnotationTarget(context, v);
|
||||
v = readAnnotationValues(v + 2, c, true,
|
||||
@@ -1025,10 +1134,10 @@ public class ClassReader {
|
||||
context.typePath, readUTF8(v, c), false));
|
||||
}
|
||||
}
|
||||
if (ANNOTATIONS && mpanns != 0) {
|
||||
if (mpanns != 0) {
|
||||
readParameterAnnotations(mv, context, mpanns, true);
|
||||
}
|
||||
if (ANNOTATIONS && impanns != 0) {
|
||||
if (impanns != 0) {
|
||||
readParameterAnnotations(mv, context, impanns, false);
|
||||
}
|
||||
|
||||
@@ -1075,7 +1184,7 @@ public class ClassReader {
|
||||
int codeStart = u;
|
||||
int codeEnd = u + codeLength;
|
||||
Label[] labels = context.labels = new Label[codeLength + 2];
|
||||
readLabel(codeLength + 1, labels);
|
||||
createLabel(codeLength + 1, labels);
|
||||
while (u < codeEnd) {
|
||||
int offset = u - codeStart;
|
||||
int opcode = b[u] & 0xFF;
|
||||
@@ -1085,11 +1194,16 @@ public class ClassReader {
|
||||
u += 1;
|
||||
break;
|
||||
case ClassWriter.LABEL_INSN:
|
||||
readLabel(offset + readShort(u + 1), labels);
|
||||
createLabel(offset + readShort(u + 1), labels);
|
||||
u += 3;
|
||||
break;
|
||||
case ClassWriter.ASM_LABEL_INSN:
|
||||
createLabel(offset + readUnsignedShort(u + 1), labels);
|
||||
u += 3;
|
||||
break;
|
||||
case ClassWriter.LABELW_INSN:
|
||||
readLabel(offset + readInt(u + 1), labels);
|
||||
case ClassWriter.ASM_LABELW_INSN:
|
||||
createLabel(offset + readInt(u + 1), labels);
|
||||
u += 5;
|
||||
break;
|
||||
case ClassWriter.WIDE_INSN:
|
||||
@@ -1104,9 +1218,9 @@ public class ClassReader {
|
||||
// skips 0 to 3 padding bytes
|
||||
u = u + 4 - (offset & 3);
|
||||
// reads instruction
|
||||
readLabel(offset + readInt(u), labels);
|
||||
createLabel(offset + readInt(u), labels);
|
||||
for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) {
|
||||
readLabel(offset + readInt(u + 12), labels);
|
||||
createLabel(offset + readInt(u + 12), labels);
|
||||
u += 4;
|
||||
}
|
||||
u += 12;
|
||||
@@ -1115,9 +1229,9 @@ public class ClassReader {
|
||||
// skips 0 to 3 padding bytes
|
||||
u = u + 4 - (offset & 3);
|
||||
// reads instruction
|
||||
readLabel(offset + readInt(u), labels);
|
||||
createLabel(offset + readInt(u), labels);
|
||||
for (int i = readInt(u + 4); i > 0; --i) {
|
||||
readLabel(offset + readInt(u + 12), labels);
|
||||
createLabel(offset + readInt(u + 12), labels);
|
||||
u += 8;
|
||||
}
|
||||
u += 8;
|
||||
@@ -1147,9 +1261,9 @@ public class ClassReader {
|
||||
|
||||
// reads the try catch entries to find the labels, and also visits them
|
||||
for (int i = readUnsignedShort(u); i > 0; --i) {
|
||||
Label start = readLabel(readUnsignedShort(u + 2), labels);
|
||||
Label end = readLabel(readUnsignedShort(u + 4), labels);
|
||||
Label handler = readLabel(readUnsignedShort(u + 6), labels);
|
||||
Label start = createLabel(readUnsignedShort(u + 2), labels);
|
||||
Label end = createLabel(readUnsignedShort(u + 4), labels);
|
||||
Label handler = createLabel(readUnsignedShort(u + 6), labels);
|
||||
String type = readUTF8(items[readUnsignedShort(u + 8)], c);
|
||||
mv.visitTryCatchBlock(start, end, handler, type);
|
||||
u += 8;
|
||||
@@ -1180,13 +1294,9 @@ public class ClassReader {
|
||||
varTable = u + 8;
|
||||
for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
|
||||
int label = readUnsignedShort(v + 10);
|
||||
if (labels[label] == null) {
|
||||
readLabel(label, labels).status |= Label.DEBUG;
|
||||
}
|
||||
createDebugLabel(label, labels);
|
||||
label += readUnsignedShort(v + 12);
|
||||
if (labels[label] == null) {
|
||||
readLabel(label, labels).status |= Label.DEBUG;
|
||||
}
|
||||
createDebugLabel(label, labels);
|
||||
v += 10;
|
||||
}
|
||||
}
|
||||
@@ -1196,9 +1306,7 @@ public class ClassReader {
|
||||
if ((context.flags & SKIP_DEBUG) == 0) {
|
||||
for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
|
||||
int label = readUnsignedShort(v + 10);
|
||||
if (labels[label] == null) {
|
||||
readLabel(label, labels).status |= Label.DEBUG;
|
||||
}
|
||||
createDebugLabel(label, labels);
|
||||
Label l = labels[label];
|
||||
while (l.line > 0) {
|
||||
if (l.next == null) {
|
||||
@@ -1210,17 +1318,15 @@ public class ClassReader {
|
||||
v += 4;
|
||||
}
|
||||
}
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
|
||||
tanns = readTypeAnnotations(mv, context, u + 8, true);
|
||||
ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1
|
||||
: readUnsignedShort(tanns[0] + 1);
|
||||
} else if (ANNOTATIONS
|
||||
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
} else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
|
||||
itanns = readTypeAnnotations(mv, context, u + 8, false);
|
||||
nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1
|
||||
: readUnsignedShort(itanns[0] + 1);
|
||||
} else if (FRAMES && "StackMapTable".equals(attrName)) {
|
||||
} else if ("StackMapTable".equals(attrName)) {
|
||||
if ((context.flags & SKIP_FRAMES) == 0) {
|
||||
stackMap = u + 10;
|
||||
stackMapSize = readInt(u + 4);
|
||||
@@ -1244,7 +1350,7 @@ public class ClassReader {
|
||||
* this by parsing the stack map table without a full decoding
|
||||
* (see below).
|
||||
*/
|
||||
} else if (FRAMES && "StackMap".equals(attrName)) {
|
||||
} else if ("StackMap".equals(attrName)) {
|
||||
if ((context.flags & SKIP_FRAMES) == 0) {
|
||||
zip = false;
|
||||
stackMap = u + 10;
|
||||
@@ -1273,7 +1379,7 @@ public class ClassReader {
|
||||
u += 2;
|
||||
|
||||
// generates the first (implicit) stack map frame
|
||||
if (FRAMES && stackMap != 0) {
|
||||
if (stackMap != 0) {
|
||||
/*
|
||||
* for the first explicit frame the offset is not offset_delta + 1
|
||||
* but only offset_delta; setting the implicit frame offset to -1
|
||||
@@ -1306,14 +1412,31 @@ public class ClassReader {
|
||||
int v = readUnsignedShort(i + 1);
|
||||
if (v >= 0 && v < codeLength) {
|
||||
if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) {
|
||||
readLabel(v, labels);
|
||||
createLabel(v, labels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((context.flags & EXPAND_ASM_INSNS) != 0
|
||||
&& (context.flags & EXPAND_FRAMES) != 0) {
|
||||
// Expanding the ASM pseudo instructions can introduce F_INSERT
|
||||
// frames, even if the method does not currently have any frame.
|
||||
// Also these inserted frames must be computed by simulating the
|
||||
// effect of the bytecode instructions one by one, starting from the
|
||||
// first one and the last existing frame (or the implicit first
|
||||
// one). Finally, due to the way MethodWriter computes this (with
|
||||
// the compute = INSERTED_FRAMES option), MethodWriter needs to know
|
||||
// maxLocals before the first instruction is visited. For all these
|
||||
// reasons we always visit the implicit first frame in this case
|
||||
// (passing only maxLocals - the rest can be and is computed in
|
||||
// MethodWriter).
|
||||
mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
|
||||
}
|
||||
|
||||
// visits the instructions
|
||||
int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
|
||||
boolean insertFrame = false;
|
||||
u = codeStart;
|
||||
while (u < codeEnd) {
|
||||
int offset = u - codeStart;
|
||||
@@ -1334,7 +1457,7 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
// visits the frame for this offset, if any
|
||||
while (FRAMES && frame != null
|
||||
while (frame != null
|
||||
&& (frame.offset == offset || frame.offset == -1)) {
|
||||
// if there is a frame for this offset, makes the visitor visit
|
||||
// it, and reads the next frame if there is one.
|
||||
@@ -1346,6 +1469,9 @@ public class ClassReader {
|
||||
mv.visitFrame(frame.mode, frame.localDiff, frame.local,
|
||||
frame.stackCount, frame.stack);
|
||||
}
|
||||
// if there is already a frame for this offset, there is no
|
||||
// need to insert a new one.
|
||||
insertFrame = false;
|
||||
}
|
||||
if (frameCount > 0) {
|
||||
stackMap = readFrame(stackMap, zip, unzip, frame);
|
||||
@@ -1354,6 +1480,13 @@ public class ClassReader {
|
||||
frame = null;
|
||||
}
|
||||
}
|
||||
// inserts a frame for this offset, if requested by setting
|
||||
// insertFrame to true during the previous iteration. The actual
|
||||
// frame content will be computed in MethodWriter.
|
||||
if (insertFrame) {
|
||||
mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null);
|
||||
insertFrame = false;
|
||||
}
|
||||
|
||||
// visits the instruction at this offset
|
||||
int opcode = b[u] & 0xFF;
|
||||
@@ -1378,9 +1511,47 @@ public class ClassReader {
|
||||
u += 3;
|
||||
break;
|
||||
case ClassWriter.LABELW_INSN:
|
||||
mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]);
|
||||
mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
|
||||
+ readInt(u + 1)]);
|
||||
u += 5;
|
||||
break;
|
||||
case ClassWriter.ASM_LABEL_INSN: {
|
||||
// changes temporary opcodes 202 to 217 (inclusive), 218
|
||||
// and 219 to IFEQ ... JSR (inclusive), IFNULL and
|
||||
// IFNONNULL
|
||||
opcode = opcode < 218 ? opcode - 49 : opcode - 20;
|
||||
Label target = labels[offset + readUnsignedShort(u + 1)];
|
||||
// replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
|
||||
// <l> with IFNOTxxx <L> GOTO_W <l> L:..., where IFNOTxxx is
|
||||
// the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
|
||||
// and where <L> designates the instruction just after
|
||||
// the GOTO_W.
|
||||
if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
|
||||
mv.visitJumpInsn(opcode + 33, target);
|
||||
} else {
|
||||
opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1
|
||||
: opcode ^ 1;
|
||||
Label endif = createLabel(offset + 3, labels);
|
||||
mv.visitJumpInsn(opcode, endif);
|
||||
mv.visitJumpInsn(200, target); // GOTO_W
|
||||
// endif designates the instruction just after GOTO_W,
|
||||
// and is visited as part of the next instruction. Since
|
||||
// it is a jump target, we need to insert a frame here.
|
||||
insertFrame = true;
|
||||
}
|
||||
u += 3;
|
||||
break;
|
||||
}
|
||||
case ClassWriter.ASM_LABELW_INSN: {
|
||||
// replaces the pseudo GOTO_W instruction with a real one.
|
||||
mv.visitJumpInsn(200, labels[offset + readInt(u + 1)]);
|
||||
// The instruction just after is a jump target (because pseudo
|
||||
// GOTO_W are used in patterns IFNOTxxx <L> GOTO_W <l> L:...,
|
||||
// see MethodWriter), so we need to insert a frame here.
|
||||
insertFrame = true;
|
||||
u += 5;
|
||||
break;
|
||||
}
|
||||
case ClassWriter.WIDE_INSN:
|
||||
opcode = b[u + 1] & 0xFF;
|
||||
if (opcode == Opcodes.IINC) {
|
||||
@@ -1636,8 +1807,8 @@ public class ClassReader {
|
||||
for (int j = readUnsignedShort(u + 1); j > 0; --j) {
|
||||
int start = readUnsignedShort(u + 3);
|
||||
int length = readUnsignedShort(u + 5);
|
||||
readLabel(start, context.labels);
|
||||
readLabel(start + length, context.labels);
|
||||
createLabel(start, context.labels);
|
||||
createLabel(start + length, context.labels);
|
||||
u += 6;
|
||||
}
|
||||
u += 3;
|
||||
@@ -1716,8 +1887,8 @@ public class ClassReader {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int start = readUnsignedShort(u);
|
||||
int length = readUnsignedShort(u + 2);
|
||||
context.start[i] = readLabel(start, context.labels);
|
||||
context.end[i] = readLabel(start + length, context.labels);
|
||||
context.start[i] = createLabel(start, context.labels);
|
||||
context.end[i] = createLabel(start + length, context.labels);
|
||||
context.index[i] = readUnsignedShort(u + 4);
|
||||
u += 6;
|
||||
}
|
||||
@@ -2137,7 +2308,7 @@ public class ClassReader {
|
||||
}
|
||||
}
|
||||
frame.offset += delta + 1;
|
||||
readLabel(frame.offset, labels);
|
||||
createLabel(frame.offset, labels);
|
||||
return stackMap;
|
||||
}
|
||||
|
||||
@@ -2190,7 +2361,7 @@ public class ClassReader {
|
||||
v += 2;
|
||||
break;
|
||||
default: // Uninitialized
|
||||
frame[index] = readLabel(readUnsignedShort(v), labels);
|
||||
frame[index] = createLabel(readUnsignedShort(v), labels);
|
||||
v += 2;
|
||||
}
|
||||
return v;
|
||||
@@ -2216,6 +2387,39 @@ public class ClassReader {
|
||||
return labels[offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a label without the Label.DEBUG flag set, for the given offset.
|
||||
* The label is created with a call to {@link #readLabel} and its
|
||||
* Label.DEBUG flag is cleared.
|
||||
*
|
||||
* @param offset
|
||||
* a bytecode offset in a method.
|
||||
* @param labels
|
||||
* the already created labels, indexed by their offset.
|
||||
* @return a Label without the Label.DEBUG flag set.
|
||||
*/
|
||||
private Label createLabel(int offset, Label[] labels) {
|
||||
Label label = readLabel(offset, labels);
|
||||
label.status &= ~Label.DEBUG;
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a label with the Label.DEBUG flag set, if there is no already
|
||||
* existing label for the given offset (otherwise does nothing). The label
|
||||
* is created with a call to {@link #readLabel}.
|
||||
*
|
||||
* @param offset
|
||||
* a bytecode offset in a method.
|
||||
* @param labels
|
||||
* the already created labels, indexed by their offset.
|
||||
*/
|
||||
private void createDebugLabel(int offset, Label[] labels) {
|
||||
if (labels[offset] == null) {
|
||||
readLabel(offset, labels).status |= Label.DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start index of the attribute_info structure of this class.
|
||||
*
|
||||
@@ -2470,6 +2674,20 @@ public class ClassReader {
|
||||
return new String(buf, 0, strLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a stringish constant item (CONSTANT_Class, CONSTANT_String,
|
||||
* CONSTANT_MethodType, CONSTANT_Module or CONSTANT_Package
|
||||
* @param index
|
||||
* @param buf
|
||||
* @return
|
||||
*/
|
||||
private String readStringish(final int index, final char[] buf) {
|
||||
// computes the start index of the item in b
|
||||
// and reads the CONSTANT_Utf8 item designated by
|
||||
// the first two bytes of this item
|
||||
return readUTF8(items[readUnsignedShort(index)], buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a class constant pool item in {@link #b b}. <i>This method is
|
||||
* intended for {@link Attribute} sub classes, and is normally not needed by
|
||||
@@ -2484,16 +2702,13 @@ public class ClassReader {
|
||||
* @return the String corresponding to the specified class item.
|
||||
*/
|
||||
public String readClass(final int index, final char[] buf) {
|
||||
// computes the start index of the CONSTANT_Class item in b
|
||||
// and reads the CONSTANT_Utf8 item designated by
|
||||
// the first two bytes of this CONSTANT_Class item
|
||||
return readUTF8(items[readUnsignedShort(index)], buf);
|
||||
return readStringish(index, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a CONSTANT_Module_info item in {@code b}. This method is intended
|
||||
* for {@link Attribute} sub classes, and is normally not needed by class
|
||||
* generators or adapters.
|
||||
* Reads a module constant pool item in {@link #b b}. <i>This method is
|
||||
* intended for {@link Attribute} sub classes, and is normally not needed by
|
||||
* class generators or adapters.</i>
|
||||
*
|
||||
* @param index
|
||||
* the start index of an unsigned short value in {@link #b b},
|
||||
@@ -2503,25 +2718,25 @@ public class ClassReader {
|
||||
* sufficiently large. It is not automatically resized.
|
||||
* @return the String corresponding to the specified module item.
|
||||
*/
|
||||
public String readModule(int index, char[] buf) {
|
||||
return readUTF8(items[readUnsignedShort(index)], buf);
|
||||
public String readModule(final int index, final char[] buf) {
|
||||
return readStringish(index, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a CONSTANT_Package_info item in {@code b}. This method is
|
||||
* intended for {@link Attribute} sub slasses, and is normally not needed
|
||||
* by class generators or adapters.
|
||||
* Reads a module constant pool item in {@link #b b}. <i>This method is
|
||||
* intended for {@link Attribute} sub classes, and is normally not needed by
|
||||
* class generators or adapters.</i>
|
||||
*
|
||||
* @param index
|
||||
* the start index of an unsigned short value in {@link #b b},
|
||||
* whose value is the index of a package constant pool item.
|
||||
* whose value is the index of a module constant pool item.
|
||||
* @param buf
|
||||
* buffer to be used to read the item. This buffer must be
|
||||
* sufficiently large. It is not automatically resized.
|
||||
* @return the String corresponding to the specified package item.
|
||||
* @return the String corresponding to the specified module item.
|
||||
*/
|
||||
public String readPackage(int index, char[] buf) {
|
||||
return readUTF8(items[readUnsignedShort(index)], buf);
|
||||
public String readPackage(final int index, final char[] buf) {
|
||||
return readStringish(index, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2550,8 +2765,6 @@ public class ClassReader {
|
||||
case ClassWriter.DOUBLE:
|
||||
return Double.longBitsToDouble(readLong(index));
|
||||
case ClassWriter.CLASS:
|
||||
case ClassWriter.MODULE:
|
||||
case ClassWriter.PACKAGE:
|
||||
return Type.getObjectType(readUTF8(index, buf));
|
||||
case ClassWriter.STR:
|
||||
return readUTF8(index, buf);
|
||||
|
||||
@@ -61,7 +61,7 @@ package org.redkale.asm;
|
||||
/**
|
||||
* A visitor to visit a Java class. The methods of this class must be called in
|
||||
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
|
||||
* <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
|
||||
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
|
||||
* <tt>visitEnd</tt>.
|
||||
@@ -72,7 +72,7 @@ public abstract class ClassVisitor {
|
||||
|
||||
/**
|
||||
* The ASM API version implemented by this visitor. The value of this field
|
||||
* must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
protected final int api;
|
||||
|
||||
@@ -87,7 +87,7 @@ public abstract class ClassVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
public ClassVisitor(final int api) {
|
||||
this(api, null);
|
||||
@@ -98,15 +98,12 @@ public abstract class ClassVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
* @param cv
|
||||
* the class visitor to which this visitor must delegate method
|
||||
* calls. May be null.
|
||||
*/
|
||||
public ClassVisitor(final int api, final ClassVisitor cv) {
|
||||
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.api = api;
|
||||
this.cv = cv;
|
||||
}
|
||||
@@ -160,6 +157,28 @@ public abstract class ClassVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit the module corresponding to the class.
|
||||
* @param name
|
||||
* module name
|
||||
* @param access
|
||||
* module flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC}
|
||||
* and {@code ACC_MANDATED}.
|
||||
* @param version
|
||||
* module version or null.
|
||||
* @return a visitor to visit the module values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this module.
|
||||
*/
|
||||
public ModuleVisitor visitModule(String name, int access, String version) {
|
||||
if (api < Opcodes.ASM6) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (cv != null) {
|
||||
return cv.visitModule(name, access, version);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the enclosing class of the class. This method must be called only
|
||||
* if the class has an enclosing class.
|
||||
@@ -222,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);
|
||||
}
|
||||
|
||||
@@ -87,8 +87,8 @@ public class ClassWriter extends ClassVisitor {
|
||||
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map
|
||||
* frames are recomputed from the methods bytecode. The arguments of the
|
||||
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
|
||||
* recomputed from the bytecode. In other words, computeFrames implies
|
||||
* computeMaxs.
|
||||
* recomputed from the bytecode. In other words, COMPUTE_FRAMES implies
|
||||
* COMPUTE_MAXS.
|
||||
*
|
||||
* @see #ClassWriter(int)
|
||||
*/
|
||||
@@ -196,6 +196,27 @@ public class ClassWriter extends ClassVisitor {
|
||||
*/
|
||||
static final int WIDE_INSN = 17;
|
||||
|
||||
/**
|
||||
* The type of the ASM pseudo instructions with an unsigned 2 bytes offset
|
||||
* label (see Label#resolve).
|
||||
*/
|
||||
static final int ASM_LABEL_INSN = 18;
|
||||
|
||||
/**
|
||||
* The type of the ASM pseudo instructions with a 4 bytes offset label.
|
||||
*/
|
||||
static final int ASM_LABELW_INSN = 19;
|
||||
|
||||
/**
|
||||
* Represents a frame inserted between already existing frames. This kind of
|
||||
* frame can only be used if the frame content can be computed from the
|
||||
* previous existing frame and from the instructions between this existing
|
||||
* frame and the inserted one, without any knowledge of the type hierarchy.
|
||||
* This kind of frame is only used when an unconditional jump is inserted in
|
||||
* a method while expanding an ASM pseudo instruction (see ClassReader).
|
||||
*/
|
||||
static final int F_INSERT = 256;
|
||||
|
||||
/**
|
||||
* The instruction types of all JVM opcodes.
|
||||
*/
|
||||
@@ -284,7 +305,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
/**
|
||||
* The base value for all CONSTANT_MethodHandle constant pool items.
|
||||
* Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9
|
||||
* different items.
|
||||
* different items (from 21 to 29).
|
||||
*/
|
||||
static final int HANDLE_BASE = 20;
|
||||
|
||||
@@ -433,6 +454,11 @@ public class ClassWriter extends ClassVisitor {
|
||||
*/
|
||||
private ByteVector sourceDebug;
|
||||
|
||||
/**
|
||||
* The module attribute of this class.
|
||||
*/
|
||||
private ModuleWriter moduleWriter;
|
||||
|
||||
/**
|
||||
* The constant pool item that contains the name of the enclosing class of
|
||||
* this class.
|
||||
@@ -523,25 +549,19 @@ public class ClassWriter extends ClassVisitor {
|
||||
MethodWriter lastMethod;
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if the maximum stack size and number of local variables
|
||||
* must be automatically computed.
|
||||
* Indicates what must be automatically computed.
|
||||
*
|
||||
* @see MethodWriter#compute
|
||||
*/
|
||||
private boolean computeMaxs;
|
||||
private int compute;
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if the stack map frames must be recomputed from scratch.
|
||||
* <tt>true</tt> if some methods have wide forward jumps using ASM pseudo
|
||||
* instructions, which need to be expanded into sequences of standard
|
||||
* bytecode instructions. In this case the class is re-read and re-written
|
||||
* with a ClassReader -> ClassWriter chain to perform this transformation.
|
||||
*/
|
||||
private boolean computeFrames;
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if the stack map tables of this class are invalid. The
|
||||
* {@link MethodWriter#resizeInstructions} method cannot transform existing
|
||||
* stack map tables, and so produces potentially invalid classes when it is
|
||||
* executed. In this case the class is reread and rewritten with the
|
||||
* {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize
|
||||
* stack map tables when this option is used).
|
||||
*/
|
||||
boolean invalidFrames;
|
||||
boolean hasAsmInsns;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Static initializer
|
||||
@@ -552,11 +572,11 @@ public class ClassWriter extends ClassVisitor {
|
||||
*/
|
||||
static {
|
||||
int i;
|
||||
byte[] b = new byte[220];
|
||||
byte[] b = new byte[221];
|
||||
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
|
||||
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
|
||||
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
|
||||
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSST";
|
||||
for (i = 0; i < b.length; ++i) {
|
||||
b[i] = (byte) (s.charAt(i) - 'A');
|
||||
}
|
||||
@@ -610,8 +630,9 @@ public class ClassWriter extends ClassVisitor {
|
||||
// // temporary opcodes used internally by ASM - see Label and
|
||||
// MethodWriter
|
||||
// for (i = 202; i < 220; ++i) {
|
||||
// b[i] = LABEL_INSN;
|
||||
// b[i] = ASM_LABEL_INSN;
|
||||
// }
|
||||
// b[220] = ASM_LABELW_INSN;
|
||||
//
|
||||
// // LDC(_W) instructions
|
||||
// b[Constants.LDC] = LDC_INSN;
|
||||
@@ -644,7 +665,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* {@link #COMPUTE_FRAMES}.
|
||||
*/
|
||||
public ClassWriter(final int flags) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM6);
|
||||
index = 1;
|
||||
pool = new ByteVector();
|
||||
items = new Item[256];
|
||||
@@ -653,8 +674,9 @@ public class ClassWriter extends ClassVisitor {
|
||||
key2 = new Item();
|
||||
key3 = new Item();
|
||||
key4 = new Item();
|
||||
this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
|
||||
this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
|
||||
this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES
|
||||
: ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS
|
||||
: MethodWriter.NOTHING);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,9 +706,9 @@ public class ClassWriter extends ClassVisitor {
|
||||
* @param flags
|
||||
* option flags that can be used to modify the default behavior
|
||||
* of this class. <i>These option flags do not affect methods
|
||||
* that are copied as is in the new class. This means that the
|
||||
* maximum stack size nor the stack frames will be computed for
|
||||
* these methods</i>. See {@link #COMPUTE_MAXS},
|
||||
* that are copied as is in the new class. This means that
|
||||
* neither the maximum stack size nor the stack frames will be
|
||||
* computed for these methods</i>. See {@link #COMPUTE_MAXS},
|
||||
* {@link #COMPUTE_FRAMES}.
|
||||
*/
|
||||
public ClassWriter(final ClassReader classReader, final int flags) {
|
||||
@@ -705,9 +727,9 @@ public class ClassWriter extends ClassVisitor {
|
||||
final String[] interfaces) {
|
||||
this.version = version;
|
||||
this.access = access;
|
||||
this.name = (name == null) ? 0 : newClass(name);
|
||||
this.name = newClass(name);
|
||||
thisName = name;
|
||||
if (ClassReader.SIGNATURES && signature != null) {
|
||||
if (signature != null) {
|
||||
this.signature = newUTF8(signature);
|
||||
}
|
||||
this.superName = superName == null ? 0 : newClass(superName);
|
||||
@@ -731,6 +753,14 @@ public class ClassWriter extends ClassVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ModuleVisitor visitModule(final String name,
|
||||
final int access, final String version) {
|
||||
return moduleWriter = new ModuleWriter(this,
|
||||
newModule(name), access,
|
||||
version == null ? 0 : newUTF8(version));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void visitOuterClass(final String owner, final String name,
|
||||
final String desc) {
|
||||
@@ -743,9 +773,6 @@ public class ClassWriter extends ClassVisitor {
|
||||
@Override
|
||||
public final AnnotationVisitor visitAnnotation(final String desc,
|
||||
final boolean visible) {
|
||||
if (!ClassReader.ANNOTATIONS) {
|
||||
return null;
|
||||
}
|
||||
ByteVector bv = new ByteVector();
|
||||
// write type, and reserve space for values count
|
||||
bv.putShort(newUTF8(desc)).putShort(0);
|
||||
@@ -763,9 +790,6 @@ public class ClassWriter extends ClassVisitor {
|
||||
@Override
|
||||
public final AnnotationVisitor visitTypeAnnotation(int typeRef,
|
||||
TypePath typePath, final String desc, final boolean visible) {
|
||||
if (!ClassReader.ANNOTATIONS) {
|
||||
return null;
|
||||
}
|
||||
ByteVector bv = new ByteVector();
|
||||
// write target_type and target_info
|
||||
AnnotationWriter.putTarget(typeRef, typePath, bv);
|
||||
@@ -805,7 +829,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
// and equality tests). If so we store the index of this inner class
|
||||
// entry (plus one) in intVal. This hack allows duplicate detection in
|
||||
// O(1) time.
|
||||
Item nameItem = newClassItem(name);
|
||||
Item nameItem = newStringishItem(CLASS, name);
|
||||
if (nameItem.intVal == 0) {
|
||||
++innerClassesCount;
|
||||
innerClasses.putShort(nameItem.index);
|
||||
@@ -830,7 +854,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
public final MethodVisitor visitMethod(final int access, final String name,
|
||||
final String desc, final String signature, final String[] exceptions) {
|
||||
return new MethodWriter(this, access, name, desc, signature,
|
||||
exceptions, computeMaxs, computeFrames);
|
||||
exceptions, compute);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -874,7 +898,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
size += 8 + bootstrapMethods.length;
|
||||
newUTF8("BootstrapMethods");
|
||||
}
|
||||
if (ClassReader.SIGNATURES && signature != 0) {
|
||||
if (signature != 0) {
|
||||
++attributeCount;
|
||||
size += 8;
|
||||
newUTF8("Signature");
|
||||
@@ -912,26 +936,31 @@ public class ClassWriter extends ClassVisitor {
|
||||
size += 8 + innerClasses.length;
|
||||
newUTF8("InnerClasses");
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && anns != null) {
|
||||
if (anns != null) {
|
||||
++attributeCount;
|
||||
size += 8 + anns.getSize();
|
||||
newUTF8("RuntimeVisibleAnnotations");
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && ianns != null) {
|
||||
if (ianns != null) {
|
||||
++attributeCount;
|
||||
size += 8 + ianns.getSize();
|
||||
newUTF8("RuntimeInvisibleAnnotations");
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && tanns != null) {
|
||||
if (tanns != null) {
|
||||
++attributeCount;
|
||||
size += 8 + tanns.getSize();
|
||||
newUTF8("RuntimeVisibleTypeAnnotations");
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && itanns != null) {
|
||||
if (itanns != null) {
|
||||
++attributeCount;
|
||||
size += 8 + itanns.getSize();
|
||||
newUTF8("RuntimeInvisibleTypeAnnotations");
|
||||
}
|
||||
if (moduleWriter != null) {
|
||||
attributeCount += 1 + moduleWriter.attributeCount;
|
||||
size += 6 + moduleWriter.size + moduleWriter.attributesSize;
|
||||
newUTF8("Module");
|
||||
}
|
||||
if (attrs != null) {
|
||||
attributeCount += attrs.getCount();
|
||||
size += attrs.getSize(this, null, 0, -1, -1);
|
||||
@@ -968,7 +997,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
bootstrapMethodsCount);
|
||||
out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length);
|
||||
}
|
||||
if (ClassReader.SIGNATURES && signature != 0) {
|
||||
if (signature != 0) {
|
||||
out.putShort(newUTF8("Signature")).putInt(2).putShort(signature);
|
||||
}
|
||||
if (sourceFile != 0) {
|
||||
@@ -979,6 +1008,11 @@ public class ClassWriter extends ClassVisitor {
|
||||
out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
|
||||
out.putByteArray(sourceDebug.data, 0, len);
|
||||
}
|
||||
if (moduleWriter != null) {
|
||||
out.putShort(newUTF8("Module"));
|
||||
moduleWriter.put(out);
|
||||
moduleWriter.putAttributes(out);
|
||||
}
|
||||
if (enclosingMethodOwner != 0) {
|
||||
out.putShort(newUTF8("EnclosingMethod")).putInt(4);
|
||||
out.putShort(enclosingMethodOwner).putShort(enclosingMethod);
|
||||
@@ -997,41 +1031,46 @@ public class ClassWriter extends ClassVisitor {
|
||||
out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
|
||||
out.putByteArray(innerClasses.data, 0, innerClasses.length);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && anns != null) {
|
||||
if (anns != null) {
|
||||
out.putShort(newUTF8("RuntimeVisibleAnnotations"));
|
||||
anns.put(out);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && ianns != null) {
|
||||
if (ianns != null) {
|
||||
out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
|
||||
ianns.put(out);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && tanns != null) {
|
||||
if (tanns != null) {
|
||||
out.putShort(newUTF8("RuntimeVisibleTypeAnnotations"));
|
||||
tanns.put(out);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && itanns != null) {
|
||||
if (itanns != null) {
|
||||
out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations"));
|
||||
itanns.put(out);
|
||||
}
|
||||
if (attrs != null) {
|
||||
attrs.put(this, null, 0, -1, -1, out);
|
||||
}
|
||||
if (invalidFrames) {
|
||||
if (hasAsmInsns) {
|
||||
boolean hasFrames = false;
|
||||
mb = firstMethod;
|
||||
while (mb != null) {
|
||||
hasFrames |= mb.frameCount > 0;
|
||||
mb = (MethodWriter) mb.mv;
|
||||
}
|
||||
anns = null;
|
||||
ianns = null;
|
||||
attrs = null;
|
||||
innerClassesCount = 0;
|
||||
innerClasses = null;
|
||||
bootstrapMethodsCount = 0;
|
||||
bootstrapMethods = null;
|
||||
moduleWriter = null;
|
||||
firstField = null;
|
||||
lastField = null;
|
||||
firstMethod = null;
|
||||
lastMethod = null;
|
||||
computeMaxs = false;
|
||||
computeFrames = true;
|
||||
invalidFrames = false;
|
||||
new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES);
|
||||
compute =
|
||||
hasFrames ? MethodWriter.INSERTED_FRAMES : MethodWriter.NOTHING;
|
||||
hasAsmInsns = false;
|
||||
new ClassReader(out.data).accept(this,
|
||||
(hasFrames ? ClassReader.EXPAND_FRAMES : 0)
|
||||
| ClassReader.EXPAND_ASM_INSNS);
|
||||
return toByteArray();
|
||||
}
|
||||
return out.data;
|
||||
@@ -1078,16 +1117,16 @@ public class ClassWriter extends ClassVisitor {
|
||||
double val = ((Double) cst).doubleValue();
|
||||
return newDouble(val);
|
||||
} else if (cst instanceof String) {
|
||||
return newString((String) cst);
|
||||
return newStringishItem(STR, (String) cst);
|
||||
} else if (cst instanceof Type) {
|
||||
Type t = (Type) cst;
|
||||
int s = t.getSort();
|
||||
if (s == Type.OBJECT) {
|
||||
return newClassItem(t.getInternalName());
|
||||
return newStringishItem(CLASS, t.getInternalName());
|
||||
} else if (s == Type.METHOD) {
|
||||
return newMethodTypeItem(t.getDescriptor());
|
||||
return newStringishItem(MTYPE, t.getDescriptor());
|
||||
} else { // s == primitive type or array
|
||||
return newClassItem(t.getDescriptor());
|
||||
return newStringishItem(CLASS, t.getDescriptor());
|
||||
}
|
||||
} else if (cst instanceof Handle) {
|
||||
Handle h = (Handle) cst;
|
||||
@@ -1136,20 +1175,21 @@ public class ClassWriter extends ClassVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class reference to the constant pool of the class being build.
|
||||
* Adds a string reference, a class reference, a method type, a module
|
||||
* or a package 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 type
|
||||
* a type among STR, CLASS, MTYPE, MODULE or PACKAGE
|
||||
* @param value
|
||||
* the internal name of the class.
|
||||
* @return a new or already existing class reference item.
|
||||
* string value of the reference.
|
||||
* @return a new or already existing reference item.
|
||||
*/
|
||||
Item newClassItem(final String value) {
|
||||
key2.set(CLASS, value, null, null);
|
||||
Item newStringishItem(final int type, final String value) {
|
||||
key2.set(type, value, null, null);
|
||||
Item result = get(key2);
|
||||
if (result == null) {
|
||||
pool.put12(CLASS, newUTF8(value));
|
||||
pool.put12(type, newUTF8(value));
|
||||
result = new Item(index++, key2);
|
||||
put(result);
|
||||
}
|
||||
@@ -1167,72 +1207,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* @return the index of a new or already existing class reference item.
|
||||
*/
|
||||
public int newClass(final String value) {
|
||||
return newClassItem(value).index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a module name to the constant pool.
|
||||
*
|
||||
* 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 value
|
||||
* the module name
|
||||
* @return the index of a new or already existing module reference item.
|
||||
*/
|
||||
public int newModule(String value) {
|
||||
key2.set(MODULE, value, null, null);
|
||||
Item result = get(key2);
|
||||
if (result == null) {
|
||||
pool.put12(MODULE, newUTF8(value));
|
||||
result = new Item(index++, key2);
|
||||
put(result);
|
||||
}
|
||||
return result.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a package name to the constant pool.
|
||||
*
|
||||
* 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 value
|
||||
* the internal name of the package.
|
||||
* @return the index of a new or already existing package reference item.
|
||||
*/
|
||||
public int newPackage(String value) {
|
||||
key2.set(PACKAGE, value, null, null);
|
||||
Item result = get(key2);
|
||||
if (result == null) {
|
||||
pool.put12(PACKAGE, newUTF8(value));
|
||||
result = new Item(index++, key2);
|
||||
put(result);
|
||||
}
|
||||
return result.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a method type reference 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 methodDesc
|
||||
* method descriptor of the method type.
|
||||
* @return a new or already existing method type reference item.
|
||||
*/
|
||||
Item newMethodTypeItem(final String methodDesc) {
|
||||
key2.set(MTYPE, methodDesc, null, null);
|
||||
Item result = get(key2);
|
||||
if (result == null) {
|
||||
pool.put12(MTYPE, newUTF8(methodDesc));
|
||||
result = new Item(index++, key2);
|
||||
put(result);
|
||||
}
|
||||
return result;
|
||||
return newStringishItem(CLASS, value).index;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1247,7 +1222,37 @@ public class ClassWriter extends ClassVisitor {
|
||||
* item.
|
||||
*/
|
||||
public int newMethodType(final String methodDesc) {
|
||||
return newMethodTypeItem(methodDesc).index;
|
||||
return newStringishItem(MTYPE, methodDesc).index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a module reference 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 moduleName
|
||||
* name of the module.
|
||||
* @return the index of a new or already existing module reference
|
||||
* item.
|
||||
*/
|
||||
public int newModule(final String moduleName) {
|
||||
return newStringishItem(MODULE, moduleName).index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a package reference 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 packageName
|
||||
* name of the package in its internal form.
|
||||
* @return the index of a new or already existing module reference
|
||||
* item.
|
||||
*/
|
||||
public int newPackage(final String packageName) {
|
||||
return newStringishItem(PACKAGE, packageName).index;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1292,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
|
||||
@@ -1628,25 +1601,6 @@ public class ClassWriter extends ClassVisitor {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string to the constant pool of the class being build. Does nothing
|
||||
* if the constant pool already contains a similar item.
|
||||
*
|
||||
* @param value
|
||||
* the String value.
|
||||
* @return a new or already existing string item.
|
||||
*/
|
||||
private Item newString(final String value) {
|
||||
key2.set(STR, value, null, null);
|
||||
Item result = get(key2);
|
||||
if (result == null) {
|
||||
pool.put12(STR, newUTF8(value));
|
||||
result = new Item(index++, key2);
|
||||
put(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a name and type to the constant pool of the class being build. Does
|
||||
* nothing if the constant pool already contains a similar item. <i>This
|
||||
|
||||
85
src/org/redkale/asm/CurrentFrame.java
Normal file
85
src/org/redkale/asm/CurrentFrame.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* ASM: a very small and fast Java bytecode manipulation framework
|
||||
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* Information about the input stack map frame at the "current" instruction of a
|
||||
* method. This is implemented as a Frame subclass for a "basic block"
|
||||
* containing only one instruction.
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
*/
|
||||
class CurrentFrame extends Frame {
|
||||
|
||||
/**
|
||||
* Sets this CurrentFrame to the input stack map frame of the next "current"
|
||||
* instruction, i.e. the instruction just after the given one. It is assumed
|
||||
* that the value of this object when this method is called is the stack map
|
||||
* frame status just before the given instruction is executed.
|
||||
*/
|
||||
@Override
|
||||
void execute(int opcode, int arg, ClassWriter cw, Item item) {
|
||||
super.execute(opcode, arg, cw, item);
|
||||
Frame successor = new Frame();
|
||||
merge(cw, successor, 0);
|
||||
set(successor);
|
||||
owner.inputStackTop = 0;
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public abstract class FieldVisitor {
|
||||
|
||||
/**
|
||||
* The ASM API version implemented by this visitor. The value of this field
|
||||
* must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
protected final int api;
|
||||
|
||||
@@ -84,7 +84,7 @@ public abstract class FieldVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
public FieldVisitor(final int api) {
|
||||
this(api, null);
|
||||
@@ -95,15 +95,12 @@ public abstract class FieldVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
* @param fv
|
||||
* the field visitor to which this visitor must delegate method
|
||||
* calls. May be null.
|
||||
*/
|
||||
public FieldVisitor(final int api, final FieldVisitor fv) {
|
||||
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ final class FieldWriter extends FieldVisitor {
|
||||
*/
|
||||
FieldWriter(final ClassWriter cw, final int access, final String name,
|
||||
final String desc, final String signature, final Object value) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM6);
|
||||
if (cw.firstField == null) {
|
||||
cw.firstField = this;
|
||||
} else {
|
||||
@@ -158,7 +158,7 @@ final class FieldWriter extends FieldVisitor {
|
||||
this.access = access;
|
||||
this.name = cw.newUTF8(name);
|
||||
this.desc = cw.newUTF8(desc);
|
||||
if (ClassReader.SIGNATURES && signature != null) {
|
||||
if (signature != null) {
|
||||
this.signature = cw.newUTF8(signature);
|
||||
}
|
||||
if (value != null) {
|
||||
@@ -173,9 +173,6 @@ final class FieldWriter extends FieldVisitor {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(final String desc,
|
||||
final boolean visible) {
|
||||
if (!ClassReader.ANNOTATIONS) {
|
||||
return null;
|
||||
}
|
||||
ByteVector bv = new ByteVector();
|
||||
// write type, and reserve space for values count
|
||||
bv.putShort(cw.newUTF8(desc)).putShort(0);
|
||||
@@ -193,9 +190,6 @@ final class FieldWriter extends FieldVisitor {
|
||||
@Override
|
||||
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
|
||||
final TypePath typePath, final String desc, final boolean visible) {
|
||||
if (!ClassReader.ANNOTATIONS) {
|
||||
return null;
|
||||
}
|
||||
ByteVector bv = new ByteVector();
|
||||
// write target_type and target_info
|
||||
AnnotationWriter.putTarget(typeRef, typePath, bv);
|
||||
@@ -249,23 +243,23 @@ final class FieldWriter extends FieldVisitor {
|
||||
cw.newUTF8("Deprecated");
|
||||
size += 6;
|
||||
}
|
||||
if (ClassReader.SIGNATURES && signature != 0) {
|
||||
if (signature != 0) {
|
||||
cw.newUTF8("Signature");
|
||||
size += 8;
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && anns != null) {
|
||||
if (anns != null) {
|
||||
cw.newUTF8("RuntimeVisibleAnnotations");
|
||||
size += 8 + anns.getSize();
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && ianns != null) {
|
||||
if (ianns != null) {
|
||||
cw.newUTF8("RuntimeInvisibleAnnotations");
|
||||
size += 8 + ianns.getSize();
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && tanns != null) {
|
||||
if (tanns != null) {
|
||||
cw.newUTF8("RuntimeVisibleTypeAnnotations");
|
||||
size += 8 + tanns.getSize();
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && itanns != null) {
|
||||
if (itanns != null) {
|
||||
cw.newUTF8("RuntimeInvisibleTypeAnnotations");
|
||||
size += 8 + itanns.getSize();
|
||||
}
|
||||
@@ -299,19 +293,19 @@ final class FieldWriter extends FieldVisitor {
|
||||
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
++attributeCount;
|
||||
}
|
||||
if (ClassReader.SIGNATURES && signature != 0) {
|
||||
if (signature != 0) {
|
||||
++attributeCount;
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && anns != null) {
|
||||
if (anns != null) {
|
||||
++attributeCount;
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && ianns != null) {
|
||||
if (ianns != null) {
|
||||
++attributeCount;
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && tanns != null) {
|
||||
if (tanns != null) {
|
||||
++attributeCount;
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && itanns != null) {
|
||||
if (itanns != null) {
|
||||
++attributeCount;
|
||||
}
|
||||
if (attrs != null) {
|
||||
@@ -331,23 +325,23 @@ final class FieldWriter extends FieldVisitor {
|
||||
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
|
||||
}
|
||||
if (ClassReader.SIGNATURES && signature != 0) {
|
||||
if (signature != 0) {
|
||||
out.putShort(cw.newUTF8("Signature"));
|
||||
out.putInt(2).putShort(signature);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && anns != null) {
|
||||
if (anns != null) {
|
||||
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
|
||||
anns.put(out);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && ianns != null) {
|
||||
if (ianns != null) {
|
||||
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
|
||||
ianns.put(out);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && tanns != null) {
|
||||
if (tanns != null) {
|
||||
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
|
||||
tanns.put(out);
|
||||
}
|
||||
if (ClassReader.ANNOTATIONS && itanns != null) {
|
||||
if (itanns != null) {
|
||||
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
|
||||
itanns.put(out);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ package org.redkale.asm;
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
*/
|
||||
final class Frame {
|
||||
class Frame {
|
||||
|
||||
/*
|
||||
* Frames are computed in a two steps process: during the visit of each
|
||||
@@ -192,7 +192,7 @@ final class Frame {
|
||||
private static final int LOCAL = 0x2000000;
|
||||
|
||||
/**
|
||||
* Kind of the the types that are relative to the stack of an input stack
|
||||
* Kind of the types that are relative to the stack of an input stack
|
||||
* map frame. The value of such types is a position relatively to the top of
|
||||
* this stack.
|
||||
*/
|
||||
@@ -525,7 +525,7 @@ final class Frame {
|
||||
* When the stack map frames are completely computed, this field is the
|
||||
* actual number of types in {@link #outputStack}.
|
||||
*/
|
||||
private int outputStackTop;
|
||||
int outputStackTop;
|
||||
|
||||
/**
|
||||
* Number of types that are initialized in the basic block.
|
||||
@@ -549,6 +549,110 @@ final class Frame {
|
||||
*/
|
||||
private int[] initializations;
|
||||
|
||||
/**
|
||||
* Sets this frame to the given value.
|
||||
*
|
||||
* @param cw
|
||||
* the ClassWriter to which this label belongs.
|
||||
* @param nLocal
|
||||
* the number of local variables.
|
||||
* @param local
|
||||
* the local variable types. Primitive types are represented by
|
||||
* {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
|
||||
* {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
|
||||
* {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
|
||||
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are
|
||||
* represented by a single element). Reference types are
|
||||
* represented by String objects (representing internal names),
|
||||
* and uninitialized types by Label objects (this label
|
||||
* designates the NEW instruction that created this uninitialized
|
||||
* value).
|
||||
* @param nStack
|
||||
* the number of operand stack elements.
|
||||
* @param stack
|
||||
* the operand stack types (same format as the "local" array).
|
||||
*/
|
||||
final void set(ClassWriter cw, final int nLocal, final Object[] local,
|
||||
final int nStack, final Object[] stack) {
|
||||
int i = convert(cw, nLocal, local, inputLocals);
|
||||
while (i < local.length) {
|
||||
inputLocals[i++] = TOP;
|
||||
}
|
||||
int nStackTop = 0;
|
||||
for (int j = 0; j < nStack; ++j) {
|
||||
if (stack[j] == Opcodes.LONG || stack[j] == Opcodes.DOUBLE) {
|
||||
++nStackTop;
|
||||
}
|
||||
}
|
||||
inputStack = new int[nStack + nStackTop];
|
||||
convert(cw, nStack, stack, inputStack);
|
||||
outputStackTop = 0;
|
||||
initializationCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts types from the MethodWriter.visitFrame() format to the Frame
|
||||
* format.
|
||||
*
|
||||
* @param cw
|
||||
* the ClassWriter to which this label belongs.
|
||||
* @param nInput
|
||||
* the number of types to convert.
|
||||
* @param input
|
||||
* the types to convert. Primitive types are represented by
|
||||
* {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
|
||||
* {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
|
||||
* {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
|
||||
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are
|
||||
* represented by a single element). Reference types are
|
||||
* represented by String objects (representing internal names),
|
||||
* and uninitialized types by Label objects (this label
|
||||
* designates the NEW instruction that created this uninitialized
|
||||
* value).
|
||||
* @param output
|
||||
* where to store the converted types.
|
||||
* @return the number of output elements.
|
||||
*/
|
||||
private static int convert(ClassWriter cw, int nInput, Object[] input,
|
||||
int[] output) {
|
||||
int i = 0;
|
||||
for (int j = 0; j < nInput; ++j) {
|
||||
if (input[j] instanceof Integer) {
|
||||
output[i++] = BASE | ((Integer) input[j]).intValue();
|
||||
if (input[j] == Opcodes.LONG || input[j] == Opcodes.DOUBLE) {
|
||||
output[i++] = TOP;
|
||||
}
|
||||
} else if (input[j] instanceof String) {
|
||||
output[i++] = type(cw, Type.getObjectType((String) input[j])
|
||||
.getDescriptor());
|
||||
} else {
|
||||
output[i++] = UNINITIALIZED
|
||||
| cw.addUninitializedType("",
|
||||
((Label) input[j]).position);
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this frame to the value of the given frame. WARNING: after this
|
||||
* method is called the two frames share the same data structures. It is
|
||||
* recommended to discard the given frame f to avoid unexpected side
|
||||
* effects.
|
||||
*
|
||||
* @param f
|
||||
* The new frame value.
|
||||
*/
|
||||
final void set(final Frame f) {
|
||||
inputLocals = f.inputLocals;
|
||||
inputStack = f.inputStack;
|
||||
outputLocals = f.outputLocals;
|
||||
outputStack = f.outputStack;
|
||||
outputStackTop = f.outputStackTop;
|
||||
initializationCount = f.initializationCount;
|
||||
initializations = f.initializations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output frame local variable type at the given index.
|
||||
*
|
||||
@@ -614,7 +718,7 @@ final class Frame {
|
||||
}
|
||||
// pushes the type on the output stack
|
||||
outputStack[outputStackTop++] = type;
|
||||
// updates the maximun height reached by the output stack, if needed
|
||||
// updates the maximum height reached by the output stack, if needed
|
||||
int top = owner.inputStackTop + outputStackTop;
|
||||
if (top > owner.outputStackMax) {
|
||||
owner.outputStackMax = top;
|
||||
@@ -650,7 +754,7 @@ final class Frame {
|
||||
* a type descriptor.
|
||||
* @return the int encoding of the given type.
|
||||
*/
|
||||
private static int type(final ClassWriter cw, final String desc) {
|
||||
static int type(final ClassWriter cw, final String desc) {
|
||||
String t;
|
||||
int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
|
||||
switch (desc.charAt(index)) {
|
||||
@@ -838,7 +942,7 @@ final class Frame {
|
||||
* @param maxLocals
|
||||
* the maximum number of local variables of this method.
|
||||
*/
|
||||
void initInputFrame(final ClassWriter cw, final int access,
|
||||
final void initInputFrame(final ClassWriter cw, final int access,
|
||||
final Type[] args, final int maxLocals) {
|
||||
inputLocals = new int[maxLocals];
|
||||
inputStack = new int[0];
|
||||
@@ -981,7 +1085,7 @@ final class Frame {
|
||||
case Opcodes.AALOAD:
|
||||
pop(1);
|
||||
t1 = pop();
|
||||
push(ELEMENT_OF + t1);
|
||||
push(t1 == NULL ? t1 : ELEMENT_OF + t1);
|
||||
break;
|
||||
case Opcodes.ISTORE:
|
||||
case Opcodes.FSTORE:
|
||||
@@ -1312,7 +1416,7 @@ final class Frame {
|
||||
* @return <tt>true</tt> if the input frame of the given label has been
|
||||
* changed by this operation.
|
||||
*/
|
||||
boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
|
||||
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
|
||||
boolean changed = false;
|
||||
int i, s, dim, kind, t;
|
||||
|
||||
|
||||
@@ -99,34 +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.
|
||||
|
||||
@@ -80,6 +80,7 @@ final class Item {
|
||||
* {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
|
||||
* {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
|
||||
* {@link ClassWriter#METH}, {@link ClassWriter#IMETH},
|
||||
* {@link ClassWriter#MODULE}, {@link ClassWriter#PACKAGE},
|
||||
* {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}.
|
||||
*
|
||||
* MethodHandle constant 9 variations are stored using a range of 9 values
|
||||
@@ -239,12 +240,12 @@ final class Item {
|
||||
this.strVal3 = strVal3;
|
||||
switch (type) {
|
||||
case ClassWriter.CLASS:
|
||||
case ClassWriter.MODULE:
|
||||
case ClassWriter.PACKAGE:
|
||||
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
|
||||
case ClassWriter.UTF8:
|
||||
case ClassWriter.STR:
|
||||
case ClassWriter.MTYPE:
|
||||
case ClassWriter.MODULE:
|
||||
case ClassWriter.PACKAGE:
|
||||
case ClassWriter.TYPE_NORMAL:
|
||||
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
|
||||
return;
|
||||
|
||||
@@ -139,7 +139,8 @@ public class Label {
|
||||
/**
|
||||
* Field used to associate user information to a label. Warning: this field
|
||||
* is used by the ASM tree package. In order to use it with the ASM tree
|
||||
* package you must override the method.
|
||||
* package you must override the
|
||||
*
|
||||
*/
|
||||
public Object info;
|
||||
|
||||
@@ -388,13 +389,12 @@ public class Label {
|
||||
* the position of this label in the bytecode.
|
||||
* @param data
|
||||
* the bytecode of the method.
|
||||
* @return <tt>true</tt> if a blank that was left for this label was to
|
||||
* @return <tt>true</tt> if a blank that was left for this label was too
|
||||
* small to store the offset. In such a case the corresponding jump
|
||||
* instruction is replaced with a pseudo instruction (using unused
|
||||
* opcodes) using an unsigned two bytes offset. These pseudo
|
||||
* instructions will need to be replaced with true instructions with
|
||||
* wider offsets (4 bytes instead of 2). This is done in
|
||||
* {@link MethodWriter#resizeInstructions}.
|
||||
* instructions will be replaced with standard bytecode instructions
|
||||
* with wider offsets (4 bytes instead of 2), in ClassReader.
|
||||
* @throws IllegalArgumentException
|
||||
* if this label has already been resolved, or if it has not
|
||||
* been created by the given code writer.
|
||||
@@ -453,7 +453,7 @@ public class Label {
|
||||
* @return the first label of the series to which this label belongs.
|
||||
*/
|
||||
Label getFirst() {
|
||||
return !ClassReader.FRAMES || frame == null ? this : frame.owner;
|
||||
return frame == null ? this : frame.owner;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -32,7 +32,7 @@ public class MethodDebugVisitor {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private final Map<Label, Integer> labels = new LinkedHashMap();
|
||||
private final Map<Label, Integer> labels = new LinkedHashMap<>();
|
||||
|
||||
private static final String[] opcodes = new String[200]; //0 -18
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public abstract class MethodVisitor {
|
||||
|
||||
/**
|
||||
* The ASM API version implemented by this visitor. The value of this field
|
||||
* must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
protected final int api;
|
||||
|
||||
@@ -101,7 +101,7 @@ public abstract class MethodVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
*/
|
||||
public MethodVisitor(final int api) {
|
||||
this(api, null);
|
||||
@@ -112,15 +112,12 @@ public abstract class MethodVisitor {
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be one
|
||||
* of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
|
||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
||||
* @param mv
|
||||
* the method visitor to which this visitor must delegate method
|
||||
* calls. May be null.
|
||||
*/
|
||||
public MethodVisitor(final int api, final MethodVisitor mv) {
|
||||
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
|
||||
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,34 +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
|
||||
@@ -502,14 +465,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 +678,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 +735,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 +803,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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
219
src/org/redkale/asm/ModuleVisitor.java
Normal file
219
src/org/redkale/asm/ModuleVisitor.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* ASM: a very small and fast Java bytecode manipulation framework
|
||||
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* A visitor to visit a Java module. The methods of this class must be called in
|
||||
* the following order: <tt>visitMainClass</tt> | ( <tt>visitPackage</tt> |
|
||||
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> |
|
||||
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
|
||||
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
|
||||
* take as parameter a package name or a module name. Unlike the other names which are internal names
|
||||
* (names separated by slash), module and package names are qualified names (names separated by dot).
|
||||
*
|
||||
* @author Remi Forax
|
||||
*/
|
||||
public abstract class ModuleVisitor {
|
||||
/**
|
||||
* The ASM API version implemented by this visitor. The value of this field
|
||||
* must be {@link Opcodes#ASM6}.
|
||||
*/
|
||||
protected final int api;
|
||||
|
||||
/**
|
||||
* The module visitor to which this visitor must delegate method calls. May
|
||||
* be null.
|
||||
*/
|
||||
protected ModuleVisitor mv;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ModuleVisitor}.
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM6}.
|
||||
*/
|
||||
public ModuleVisitor(final int api) {
|
||||
this(api, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ModuleVisitor}.
|
||||
*
|
||||
* @param api
|
||||
* the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM6}.
|
||||
* @param mv
|
||||
* the module visitor to which this visitor must delegate method
|
||||
* calls. May be null.
|
||||
*/
|
||||
public ModuleVisitor(final int api, final ModuleVisitor mv) {
|
||||
if (api != Opcodes.ASM6) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.api = api;
|
||||
this.mv = mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit the main class of the current module.
|
||||
*
|
||||
* @param mainClass the internal name of the main class of the current module.
|
||||
*/
|
||||
public void visitMainClass(String mainClass) {
|
||||
if (mv != null) {
|
||||
mv.visitMainClass(mainClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a package of the current module.
|
||||
*
|
||||
* @param packaze the qualified name of a package.
|
||||
*/
|
||||
public void visitPackage(String packaze) {
|
||||
if (mv != null) {
|
||||
mv.visitPackage(packaze);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a dependence of the current module.
|
||||
*
|
||||
* @param module the qualified name of the dependence.
|
||||
* @param access the access flag of the dependence among
|
||||
* ACC_TRANSITIVE, ACC_STATIC_PHASE, ACC_SYNTHETIC
|
||||
* and ACC_MANDATED.
|
||||
* @param version the module version at compile time or null.
|
||||
*/
|
||||
public void visitRequire(String module, int access, String version) {
|
||||
if (mv != null) {
|
||||
mv.visitRequire(module, access, version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit an exported package of the current module.
|
||||
*
|
||||
* @param packaze the qualified name of the exported package.
|
||||
* @param access the access flag of the exported package,
|
||||
* valid values are among {@code ACC_SYNTHETIC} and
|
||||
* {@code ACC_MANDATED}.
|
||||
* @param modules the qualified names of the modules that can access to
|
||||
* the public classes of the exported package or
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visitExport(String packaze, int access, String... modules) {
|
||||
if (mv != null) {
|
||||
mv.visitExport(packaze, access, modules);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit an open package of the current module.
|
||||
*
|
||||
* @param packaze the qualified name of the opened package.
|
||||
* @param access the access flag of the opened package,
|
||||
* valid values are among {@code ACC_SYNTHETIC} and
|
||||
* {@code ACC_MANDATED}.
|
||||
* @param modules the qualified names of the modules that can use deep
|
||||
* reflection to the classes of the open package or
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visitOpen(String packaze, int access, String... modules) {
|
||||
if (mv != null) {
|
||||
mv.visitOpen(packaze, access, modules);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a service used by the current module.
|
||||
* The name must be the internal name of an interface or a class.
|
||||
*
|
||||
* @param service the internal name of the service.
|
||||
*/
|
||||
public void visitUse(String service) {
|
||||
if (mv != null) {
|
||||
mv.visitUse(service);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit an implementation of a service.
|
||||
*
|
||||
* @param service the internal name of the service
|
||||
* @param providers the internal names of the implementations
|
||||
* of the service (there is at least one provider).
|
||||
*/
|
||||
public void visitProvide(String service, String... providers) {
|
||||
if (mv != null) {
|
||||
mv.visitProvide(service, providers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the end of the module. This method, which is the last one to be
|
||||
* called, is used to inform the visitor that everything have been visited.
|
||||
*/
|
||||
public void visitEnd() {
|
||||
if (mv != null) {
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
322
src/org/redkale/asm/ModuleWriter.java
Normal file
322
src/org/redkale/asm/ModuleWriter.java
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* ASM: a very small and fast Java bytecode manipulation framework
|
||||
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* @author Remi Forax
|
||||
*/
|
||||
final class ModuleWriter extends ModuleVisitor {
|
||||
/**
|
||||
* The class writer to which this Module attribute must be added.
|
||||
*/
|
||||
private final ClassWriter cw;
|
||||
|
||||
/**
|
||||
* size in byte of the Module attribute.
|
||||
*/
|
||||
int size;
|
||||
|
||||
/**
|
||||
* Number of attributes associated with the current module
|
||||
* (Version, ConcealPackages, etc)
|
||||
*/
|
||||
int attributeCount;
|
||||
|
||||
/**
|
||||
* Size in bytes of the attributes associated with the current module
|
||||
*/
|
||||
int attributesSize;
|
||||
|
||||
/**
|
||||
* module name index in the constant pool
|
||||
*/
|
||||
private final int name;
|
||||
|
||||
/**
|
||||
* module access flags
|
||||
*/
|
||||
private final int access;
|
||||
|
||||
/**
|
||||
* module version index in the constant pool or 0
|
||||
*/
|
||||
private final int version;
|
||||
|
||||
/**
|
||||
* module main class index in the constant pool or 0
|
||||
*/
|
||||
private int mainClass;
|
||||
|
||||
/**
|
||||
* number of packages
|
||||
*/
|
||||
private int packageCount;
|
||||
|
||||
/**
|
||||
* The packages in bytecode form. This byte vector only contains
|
||||
* the items themselves, the number of items is store in packageCount
|
||||
*/
|
||||
private ByteVector packages;
|
||||
|
||||
/**
|
||||
* number of requires items
|
||||
*/
|
||||
private int requireCount;
|
||||
|
||||
/**
|
||||
* The requires items in bytecode form. This byte vector only contains
|
||||
* the items themselves, the number of items is store in requireCount
|
||||
*/
|
||||
private ByteVector requires;
|
||||
|
||||
/**
|
||||
* number of exports items
|
||||
*/
|
||||
private int exportCount;
|
||||
|
||||
/**
|
||||
* The exports items in bytecode form. This byte vector only contains
|
||||
* the items themselves, the number of items is store in exportCount
|
||||
*/
|
||||
private ByteVector exports;
|
||||
|
||||
/**
|
||||
* number of opens items
|
||||
*/
|
||||
private int openCount;
|
||||
|
||||
/**
|
||||
* The opens items in bytecode form. This byte vector only contains
|
||||
* the items themselves, the number of items is store in openCount
|
||||
*/
|
||||
private ByteVector opens;
|
||||
|
||||
/**
|
||||
* number of uses items
|
||||
*/
|
||||
private int useCount;
|
||||
|
||||
/**
|
||||
* The uses items in bytecode form. This byte vector only contains
|
||||
* the items themselves, the number of items is store in useCount
|
||||
*/
|
||||
private ByteVector uses;
|
||||
|
||||
/**
|
||||
* number of provides items
|
||||
*/
|
||||
private int provideCount;
|
||||
|
||||
/**
|
||||
* The uses provides in bytecode form. This byte vector only contains
|
||||
* the items themselves, the number of items is store in provideCount
|
||||
*/
|
||||
private ByteVector provides;
|
||||
|
||||
ModuleWriter(final ClassWriter cw, final int name,
|
||||
final int access, final int version) {
|
||||
super(Opcodes.ASM6);
|
||||
this.cw = cw;
|
||||
this.size = 16; // name + access + version + 5 counts
|
||||
this.name = name;
|
||||
this.access = access;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMainClass(String mainClass) {
|
||||
if (this.mainClass == 0) { // protect against several calls to visitMainClass
|
||||
cw.newUTF8("ModuleMainClass");
|
||||
attributeCount++;
|
||||
attributesSize += 8;
|
||||
}
|
||||
this.mainClass = cw.newClass(mainClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPackage(String packaze) {
|
||||
if (packages == null) {
|
||||
// protect against several calls to visitPackage
|
||||
cw.newUTF8("ModulePackages");
|
||||
packages = new ByteVector();
|
||||
attributeCount++;
|
||||
attributesSize += 8;
|
||||
}
|
||||
packages.putShort(cw.newPackage(packaze));
|
||||
packageCount++;
|
||||
attributesSize += 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRequire(String module, int access, String version) {
|
||||
if (requires == null) {
|
||||
requires = new ByteVector();
|
||||
}
|
||||
requires.putShort(cw.newModule(module))
|
||||
.putShort(access)
|
||||
.putShort(version == null? 0: cw.newUTF8(version));
|
||||
requireCount++;
|
||||
size += 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExport(String packaze, int access, String... modules) {
|
||||
if (exports == null) {
|
||||
exports = new ByteVector();
|
||||
}
|
||||
exports.putShort(cw.newPackage(packaze)).putShort(access);
|
||||
if (modules == null) {
|
||||
exports.putShort(0);
|
||||
size += 6;
|
||||
} else {
|
||||
exports.putShort(modules.length);
|
||||
for(String module: modules) {
|
||||
exports.putShort(cw.newModule(module));
|
||||
}
|
||||
size += 6 + 2 * modules.length;
|
||||
}
|
||||
exportCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOpen(String packaze, int access, String... modules) {
|
||||
if (opens == null) {
|
||||
opens = new ByteVector();
|
||||
}
|
||||
opens.putShort(cw.newPackage(packaze)).putShort(access);
|
||||
if (modules == null) {
|
||||
opens.putShort(0);
|
||||
size += 6;
|
||||
} else {
|
||||
opens.putShort(modules.length);
|
||||
for(String module: modules) {
|
||||
opens.putShort(cw.newModule(module));
|
||||
}
|
||||
size += 6 + 2 * modules.length;
|
||||
}
|
||||
openCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUse(String service) {
|
||||
if (uses == null) {
|
||||
uses = new ByteVector();
|
||||
}
|
||||
uses.putShort(cw.newClass(service));
|
||||
useCount++;
|
||||
size += 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProvide(String service, String... providers) {
|
||||
if (provides == null) {
|
||||
provides = new ByteVector();
|
||||
}
|
||||
provides.putShort(cw.newClass(service));
|
||||
provides.putShort(providers.length);
|
||||
for(String provider: providers) {
|
||||
provides.putShort(cw.newClass(provider));
|
||||
}
|
||||
provideCount++;
|
||||
size += 4 + 2 * providers.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// empty
|
||||
}
|
||||
|
||||
void putAttributes(ByteVector out) {
|
||||
if (mainClass != 0) {
|
||||
out.putShort(cw.newUTF8("ModuleMainClass")).putInt(2).putShort(mainClass);
|
||||
}
|
||||
if (packages != null) {
|
||||
out.putShort(cw.newUTF8("ModulePackages"))
|
||||
.putInt(2 + 2 * packageCount)
|
||||
.putShort(packageCount)
|
||||
.putByteArray(packages.data, 0, packages.length);
|
||||
}
|
||||
}
|
||||
|
||||
void put(ByteVector out) {
|
||||
out.putInt(size);
|
||||
out.putShort(name).putShort(access).putShort(version);
|
||||
out.putShort(requireCount);
|
||||
if (requires != null) {
|
||||
out.putByteArray(requires.data, 0, requires.length);
|
||||
}
|
||||
out.putShort(exportCount);
|
||||
if (exports != null) {
|
||||
out.putByteArray(exports.data, 0, exports.length);
|
||||
}
|
||||
out.putShort(openCount);
|
||||
if (opens != null) {
|
||||
out.putByteArray(opens.data, 0, opens.length);
|
||||
}
|
||||
out.putShort(useCount);
|
||||
if (uses != null) {
|
||||
out.putByteArray(uses.data, 0, uses.length);
|
||||
}
|
||||
out.putShort(provideCount);
|
||||
if (provides != null) {
|
||||
out.putByteArray(provides.data, 0, provides.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,33 @@
|
||||
/*
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* ASM: a very small and fast Java bytecode manipulation framework
|
||||
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
* All rights reserved.
|
||||
@@ -41,25 +70,22 @@ package org.redkale.asm;
|
||||
* @author Eric Bruneton
|
||||
* @author Eugene Kuleshov
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // for Integer(int) constructor
|
||||
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;
|
||||
|
||||
// versions
|
||||
|
||||
int V1_1 = 3 << 16 | 45;
|
||||
int V1_2 = 0 << 16 | 46;
|
||||
int V1_3 = 0 << 16 | 47;
|
||||
int V1_4 = 0 << 16 | 48;
|
||||
int V1_5 = 0 << 16 | 49;
|
||||
int V1_6 = 0 << 16 | 50;
|
||||
int V1_7 = 0 << 16 | 51;
|
||||
int V1_8 = 0 << 16 | 52;
|
||||
int V1_9 = 0 << 16 | 53;
|
||||
int V9 = 0 << 16 | 53;
|
||||
int V10 = 0 << 16 | 54;
|
||||
|
||||
// access flags
|
||||
|
||||
@@ -70,18 +96,23 @@ public interface Opcodes {
|
||||
int ACC_FINAL = 0x0010; // class, field, method, parameter
|
||||
int ACC_SUPER = 0x0020; // class
|
||||
int ACC_SYNCHRONIZED = 0x0020; // method
|
||||
int ACC_OPEN = 0x0020; // module
|
||||
int ACC_TRANSITIVE = 0x0020; // module requires
|
||||
int ACC_VOLATILE = 0x0040; // field
|
||||
int ACC_BRIDGE = 0x0040; // method
|
||||
int ACC_STATIC_PHASE = 0x0040; // module requires
|
||||
int ACC_VARARGS = 0x0080; // method
|
||||
int ACC_TRANSIENT = 0x0080; // field
|
||||
int ACC_NATIVE = 0x0100; // method
|
||||
int ACC_INTERFACE = 0x0200; // class
|
||||
int ACC_ABSTRACT = 0x0400; // class, method
|
||||
int ACC_STRICT = 0x0800; // method
|
||||
int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter
|
||||
int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module *
|
||||
int ACC_ANNOTATION = 0x2000; // class
|
||||
int ACC_ENUM = 0x4000; // class(?) field inner
|
||||
int ACC_MANDATED = 0x8000; // parameter
|
||||
int ACC_MANDATED = 0x8000; // parameter, module, module *
|
||||
int ACC_MODULE = 0x8000; // class
|
||||
|
||||
|
||||
// ASM specific pseudo access flags
|
||||
|
||||
@@ -148,15 +179,17 @@ public interface Opcodes {
|
||||
*/
|
||||
int F_SAME1 = 4;
|
||||
|
||||
// For reference comparison purposes, construct new instances
|
||||
// instead of using valueOf() or autoboxing.
|
||||
Integer TOP = new Integer(0);
|
||||
Integer INTEGER = new Integer(1);
|
||||
Integer FLOAT = new Integer(2);
|
||||
Integer DOUBLE = new Integer(3);
|
||||
Integer LONG = new Integer(4);
|
||||
Integer NULL = new Integer(5);
|
||||
Integer UNINITIALIZED_THIS = new Integer(6);
|
||||
// Do not try to change the following code to use auto-boxing,
|
||||
// these values are compared by reference and not by value
|
||||
// The constructor of Integer was deprecated in 9
|
||||
// but we are stuck with it by backward compatibility
|
||||
@SuppressWarnings("deprecation") Integer TOP = new Integer(0);
|
||||
@SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
|
||||
@SuppressWarnings("deprecation") Integer FLOAT = new Integer(2);
|
||||
@SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3);
|
||||
@SuppressWarnings("deprecation") Integer LONG = new Integer(4);
|
||||
@SuppressWarnings("deprecation") Integer NULL = new Integer(5);
|
||||
@SuppressWarnings("deprecation") Integer UNINITIALIZED_THIS = new Integer(6);
|
||||
|
||||
// opcodes // visit method (- = idem)
|
||||
|
||||
|
||||
@@ -406,7 +406,16 @@ public class Type {
|
||||
*/
|
||||
public static Type getReturnType(final String methodDescriptor) {
|
||||
char[] buf = methodDescriptor.toCharArray();
|
||||
return getType(buf, methodDescriptor.indexOf(')') + 1);
|
||||
int off = 1;
|
||||
while (true) {
|
||||
char car = buf[off++];
|
||||
if (car == ')') {
|
||||
return getType(buf, off);
|
||||
} else if (car == 'L') {
|
||||
while (buf[off++] != ';') {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,62 @@
|
||||
/*
|
||||
* 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.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* ASM: a very small and fast Java bytecode manipulation framework
|
||||
* Copyright (c) 2000-2013 INRIA, France Telecom
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.redkale.asm;
|
||||
|
||||
/**
|
||||
|
||||
@@ -96,6 +96,11 @@ public final class Application {
|
||||
*/
|
||||
public static final String RESNAME_SERVER_EXECUTOR = Server.RESNAME_SERVER_EXECUTOR;
|
||||
|
||||
/**
|
||||
* 当前Server的ResourceFactory
|
||||
*/
|
||||
public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY;
|
||||
|
||||
//本地IP地址
|
||||
final InetAddress localAddress;
|
||||
|
||||
@@ -249,8 +254,8 @@ public final class Application {
|
||||
TransportStrategy strategy = null;
|
||||
int bufferCapacity = 32 * 1024;
|
||||
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
|
||||
int readTimeoutSecond = TransportFactory.DEFAULT_READTIMEOUTSECOND;
|
||||
int writeTimeoutSecond = TransportFactory.DEFAULT_WRITETIMEOUTSECOND;
|
||||
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
|
||||
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
|
||||
AtomicLong createBufferCounter = new AtomicLong();
|
||||
AtomicLong cycleBufferCounter = new AtomicLong();
|
||||
if (resources != null) {
|
||||
@@ -260,8 +265,8 @@ public final class Application {
|
||||
if (transportConf != null) {
|
||||
//--------------transportBufferPool-----------
|
||||
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 8 * 1024);
|
||||
readTimeoutSecond = transportConf.getIntValue("readTimeoutSecond", readTimeoutSecond);
|
||||
writeTimeoutSecond = transportConf.getIntValue("writeTimeoutSecond", writeTimeoutSecond);
|
||||
readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds);
|
||||
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
|
||||
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
|
||||
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
|
||||
final int capacity = bufferCapacity;
|
||||
@@ -275,7 +280,7 @@ public final class Application {
|
||||
try {
|
||||
final String strategyClass = transportConf.getValue("strategy");
|
||||
if (strategyClass != null && !strategyClass.isEmpty()) {
|
||||
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).newInstance();
|
||||
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).getDeclaredConstructor().newInstance();
|
||||
}
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
|
||||
@@ -314,8 +319,10 @@ public final class Application {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSecond, writeTimeoutSecond, strategy);
|
||||
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"));
|
||||
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
|
||||
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.poolmaxconns", "100"))
|
||||
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"))
|
||||
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30"));
|
||||
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
|
||||
Thread.currentThread().setContextClassLoader(this.classLoader);
|
||||
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
|
||||
@@ -354,8 +361,11 @@ public final class Application {
|
||||
}
|
||||
|
||||
public void init() throws Exception {
|
||||
System.setProperty("sun.nio.ch.internalThreadPoolSize", "" + Runtime.getRuntime().availableProcessors() * 4);
|
||||
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
|
||||
System.setProperty("net.transport.poolmaxconns", "100");
|
||||
System.setProperty("net.transport.pinginterval", "30");
|
||||
System.setProperty("net.transport.checkinterval", "30");
|
||||
System.setProperty("convert.bson.tiny", "true");
|
||||
System.setProperty("convert.json.tiny", "true");
|
||||
System.setProperty("convert.bson.pool.size", "128");
|
||||
@@ -427,7 +437,8 @@ public final class Application {
|
||||
if (type == Application.class) {
|
||||
field.set(src, application);
|
||||
} else if (type == ResourceFactory.class) {
|
||||
field.set(src, res.name().equalsIgnoreCase("server") ? rf : (res.name().isEmpty() ? application.resourceFactory : null));
|
||||
boolean serv = RESNAME_SERVER_RESFACTORY.equals(res.name()) || res.name().equalsIgnoreCase("server");
|
||||
field.set(src, serv ? rf : (res.name().isEmpty() ? application.resourceFactory : null));
|
||||
} else if (type == TransportFactory.class) {
|
||||
field.set(src, application.sncpTransportFactory);
|
||||
} else if (type == NodeSncpServer.class) {
|
||||
@@ -502,7 +513,8 @@ public final class Application {
|
||||
if (listenClass.isEmpty()) continue;
|
||||
Class clazz = classLoader.loadClass(listenClass);
|
||||
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
|
||||
ApplicationListener listener = (ApplicationListener) clazz.newInstance();
|
||||
@SuppressWarnings("unchecked")
|
||||
ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance();
|
||||
listener.init(config);
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
@@ -594,15 +606,15 @@ public final class Application {
|
||||
buffer.flip();
|
||||
channel.write(buffer);
|
||||
buffer.clear();
|
||||
channel.configureBlocking(false);
|
||||
channel.configureBlocking(true);
|
||||
try {
|
||||
channel.read(buffer);
|
||||
buffer.flip();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
channel.close();
|
||||
logger.info(new String(bytes));
|
||||
Thread.sleep(500);
|
||||
logger.info("Send: " + command + ", Reply: " + new String(bytes));
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof PortUnreachableException) {
|
||||
if ("APIDOC".equalsIgnoreCase(command)) {
|
||||
|
||||
@@ -266,7 +266,7 @@ public class LogFileHandler extends Handler {
|
||||
try {
|
||||
if (filterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
|
||||
setFilter((Filter) clz.newInstance());
|
||||
setFilter((Filter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
@@ -274,7 +274,7 @@ public class LogFileHandler extends Handler {
|
||||
try {
|
||||
if (formatterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
|
||||
setFormatter((Formatter) clz.newInstance());
|
||||
setFormatter((Formatter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
@@ -56,16 +56,19 @@ public class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Service> createServiceClassFilter() {
|
||||
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Filter> createFilterClassFilter() {
|
||||
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Servlet> createServletClassFilter() {
|
||||
return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
|
||||
}
|
||||
@@ -87,6 +90,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
|
||||
}
|
||||
@@ -119,8 +123,8 @@ public class NodeHttpServer extends NodeServer {
|
||||
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
||||
}
|
||||
resourceFactory.inject(nodeService, self);
|
||||
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
||||
field.set(src, nodeService);
|
||||
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
|
||||
@@ -136,7 +140,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;
|
||||
final HttpFilter filter = clazz.newInstance();
|
||||
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(filter, this);
|
||||
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
|
||||
this.httpServer.addHttpFilter(filter, filterConf);
|
||||
@@ -172,7 +176,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) continue;
|
||||
WebServlet ws = clazz.getAnnotation(WebServlet.class);
|
||||
if (ws == null || ws.value().length == 0) continue;
|
||||
final HttpServlet servlet = clazz.newInstance();
|
||||
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(servlet, this);
|
||||
final String[] mappings = ws.value();
|
||||
String pref = ws.repair() ? prefix : "";
|
||||
@@ -312,7 +316,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
if (finest) logger.finest(threadName + " " + stype.getName() + " create RestWebSocketServlet " + servlet);
|
||||
if (finest) logger.finest(threadName + " " + stype.getName() + " create a RestWebSocketServlet");
|
||||
if (ss != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
|
||||
@@ -19,10 +19,9 @@ import javax.annotation.*;
|
||||
import javax.persistence.Transient;
|
||||
import static org.redkale.boot.Application.*;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.convert.bson.*;
|
||||
import org.redkale.net.Filter;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.WebSocketServlet;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.source.*;
|
||||
@@ -96,6 +95,7 @@ public abstract class NodeServer {
|
||||
this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
|
||||
Thread.currentThread().setContextClassLoader(this.serverClassLoader);
|
||||
this.serverThread = Thread.currentThread();
|
||||
this.server.setServerClassLoader(serverClassLoader);
|
||||
}
|
||||
|
||||
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
|
||||
@@ -149,7 +149,7 @@ public abstract class NodeServer {
|
||||
String interceptorClass = this.serverConf.getValue("interceptor", "");
|
||||
if (!interceptorClass.isEmpty()) {
|
||||
Class clazz = serverClassLoader.loadClass(interceptorClass);
|
||||
this.interceptor = (NodeInterceptor) clazz.newInstance();
|
||||
this.interceptor = (NodeInterceptor) clazz.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
|
||||
ClassFilter<Service> serviceFilter = createServiceClassFilter();
|
||||
@@ -231,7 +231,14 @@ public abstract class NodeServer {
|
||||
boolean needinit = true;
|
||||
if (sourceConf != null) {
|
||||
final Class sourceType = serverClassLoader.loadClass(sourceConf.getValue("value"));
|
||||
if (DataSource.class.isAssignableFrom(sourceType)) { // DataSource
|
||||
boolean can = false;
|
||||
for (Constructor cr : sourceType.getConstructors()) {
|
||||
if (cr.getParameterCount() == 0) {
|
||||
can = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数
|
||||
final Service srcService = (Service) src;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
@@ -273,6 +280,7 @@ public abstract class NodeServer {
|
||||
|
||||
//------------------------------------- 注册 CacheSource --------------------------------------------------------
|
||||
resourceFactory.register(new ResourceFactory.ResourceLoader() {
|
||||
@Override
|
||||
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
@@ -317,10 +325,46 @@ public abstract class NodeServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean autoNone() {
|
||||
return false;
|
||||
}
|
||||
}, CacheSource.class);
|
||||
|
||||
//------------------------------------- 注册 WebSocketNode --------------------------------------------------------
|
||||
resourceFactory.register(new ResourceFactory.ResourceLoader() {
|
||||
@Override
|
||||
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 WebSocketNode
|
||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||
if (nodeService == null) {
|
||||
final HashSet<String> groups = new HashSet<>();
|
||||
if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup);
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
|
||||
(isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService);
|
||||
}
|
||||
resourceFactory.inject(nodeService, self);
|
||||
field.set(src, nodeService);
|
||||
if (Sncp.isRemote(nodeService)) {
|
||||
remoteServices.add(nodeService);
|
||||
} else {
|
||||
if (field != null) rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
|
||||
localServices.add(nodeService);
|
||||
interceptorServices.add(nodeService);
|
||||
if (consumer != null) consumer.accept(nodeService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean autoNone() {
|
||||
return false;
|
||||
}
|
||||
}, WebSocketNode.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -340,7 +384,6 @@ public abstract class NodeServer {
|
||||
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
|
||||
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
|
||||
if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
|
||||
//if (WebSocketNode.class.isAssignableFrom(serviceImplClass)) continue;
|
||||
}
|
||||
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
|
||||
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
|
||||
@@ -457,125 +500,6 @@ public abstract class NodeServer {
|
||||
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
|
||||
}
|
||||
|
||||
//尚未完整实现, 先屏蔽, 单个Service在多个Server中存在的情况下进行缓存的方案还未考虑清楚
|
||||
@SuppressWarnings("unchecked")
|
||||
private void loadPersistData() throws Exception {
|
||||
File home = application.getHome();
|
||||
if (home == null || !home.isDirectory()) return;
|
||||
File cachedir = new File(home, "cache");
|
||||
if (!cachedir.isDirectory()) return;
|
||||
int port = this.server.getSocketAddress().getPort();
|
||||
final String prefix = "persist-" + port + "-";
|
||||
final BsonConvert convert = BsonFactory.create().skipAllIgnore(true).getConvert();
|
||||
synchronized (this.application) {
|
||||
for (final File file : cachedir.listFiles((dir, name) -> name.startsWith(prefix))) {
|
||||
if (!file.getName().endsWith(".bat")) continue;
|
||||
String classAndResname = file.getName().substring(prefix.length(), file.getName().length() - 4); //去掉尾部的.bat
|
||||
int pos = classAndResname.indexOf('-');
|
||||
String servtype = pos > 0 ? classAndResname.substring(0, pos) : classAndResname;
|
||||
String resname = pos > 0 ? classAndResname.substring(pos + 1) : "";
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int b;
|
||||
while ((b = in.read()) != '\n') out.write(b);
|
||||
final String[] fieldNames = out.toString().split(",");
|
||||
int timeout = (int) ((System.currentTimeMillis() - file.lastModified()) / 1000);
|
||||
for (final Service service : this.localServices) {
|
||||
if (!servtype.equals(Sncp.getResourceType(service).getName())) continue;
|
||||
if (!resname.equals(Sncp.getResourceName(service))) continue;
|
||||
for (final String fieldName : fieldNames) {
|
||||
Field field = null;
|
||||
Class clzz = service.getClass();
|
||||
do {
|
||||
try {
|
||||
field = clzz.getDeclaredField(fieldName);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
} while ((clzz = clzz.getSuperclass()) != Object.class);
|
||||
field.setAccessible(true);
|
||||
Object val = convert.convertFrom(field.getGenericType(), in);
|
||||
Persist persist = field.getAnnotation(Persist.class);
|
||||
if (persist.timeout() == 0 || persist.timeout() >= timeout) {
|
||||
if (Modifier.isFinal(field.getModifiers())) {
|
||||
if (Map.class.isAssignableFrom(field.getType())) {
|
||||
((Map) field.get(service)).putAll((Map) val);
|
||||
} else if (Collection.class.isAssignableFrom(field.getType())) {
|
||||
((Collection) field.get(service)).addAll((Collection) val);
|
||||
}
|
||||
} else {
|
||||
field.set(service, val);
|
||||
}
|
||||
}
|
||||
if (in.read() != '\n') logger.log(Level.SEVERE, servtype + "'s [" + resname + "] load value error");
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//尚未完整实现, 先屏蔽
|
||||
@SuppressWarnings("unchecked")
|
||||
private void savePersistData() throws IOException {
|
||||
File home = application.getHome();
|
||||
if (home == null || !home.isDirectory()) return;
|
||||
File cachedir = new File(home, "cache");
|
||||
int port = this.server.getSocketAddress().getPort();
|
||||
final String prefix = "persist-" + port + "-";
|
||||
final BsonConvert convert = BsonFactory.create().skipAllIgnore(true).getConvert();
|
||||
for (final Service service : this.localServices) {
|
||||
Class clzz = service.getClass();
|
||||
final Set<String> fieldNameSet = new HashSet<>();
|
||||
final List<Field> fields = new ArrayList<>();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
do {
|
||||
for (Field field : clzz.getDeclaredFields()) {
|
||||
if (field.getAnnotation(Persist.class) == null) continue;
|
||||
if (fieldNameSet.contains(field.getName())) continue;
|
||||
if (Modifier.isStatic(field.getModifiers())) throw new RuntimeException(field + " cannot static on @" + Persist.class.getName() + " in " + clzz.getName());
|
||||
if (Modifier.isFinal(field.getModifiers()) && !Map.class.isAssignableFrom(field.getType()) && !Collection.class.isAssignableFrom(field.getType())) {
|
||||
throw new RuntimeException(field + " cannot final on @" + Persist.class.getName() + " in " + clzz.getName());
|
||||
}
|
||||
fieldNameSet.add(field.getName());
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
if (field.get(service) == null) continue;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, field + " get value error", e);
|
||||
continue;
|
||||
}
|
||||
fields.add(field);
|
||||
if (sb.length() > 0) sb.append(',');
|
||||
sb.append(field.getName());
|
||||
}
|
||||
} while ((clzz = clzz.getSuperclass()) != Object.class);
|
||||
|
||||
if (fields.isEmpty()) continue; //没有数据需要缓存
|
||||
// synchronized (this.application.localServices) {
|
||||
// if (this.application.localServices.contains(service)) continue;
|
||||
// this.application.localServices.add(service);
|
||||
// }
|
||||
if (!cachedir.isDirectory()) cachedir.mkdirs();
|
||||
String resname = Sncp.getResourceName(service);
|
||||
FileOutputStream out = new FileOutputStream(new File(cachedir, prefix + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname)) + ".bat"));
|
||||
out.write(sb.toString().getBytes());
|
||||
out.write('\n');
|
||||
for (Field field : fields) {
|
||||
Object val = null;
|
||||
try {
|
||||
val = field.get(service);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, field + " save value error", e);
|
||||
}
|
||||
convert.convertTo(out, field.getGenericType(), val);
|
||||
out.write('\n');
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ClassFilter<Filter> createFilterClassFilter();
|
||||
|
||||
protected abstract ClassFilter<Servlet> createServletClassFilter();
|
||||
@@ -708,9 +632,12 @@ public abstract class NodeServer {
|
||||
public void shutdown() throws IOException {
|
||||
if (interceptor != null) interceptor.preShutdown(this);
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
localServices.forEach(y -> {
|
||||
long s = System.currentTimeMillis();
|
||||
if (finest) logger.finest(y + " is destroying");
|
||||
y.destroy(Sncp.getConf(y));
|
||||
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);
|
||||
|
||||
@@ -93,7 +93,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;
|
||||
final SncpFilter filter = clazz.newInstance();
|
||||
final SncpFilter filter = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(filter, this);
|
||||
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
|
||||
this.sncpServer.addSncpFilter(filter, filterConf);
|
||||
|
||||
@@ -24,16 +24,19 @@ public class NodeWatchServer extends NodeHttpServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Service> createServiceClassFilter() {
|
||||
return createClassFilter(this.sncpGroup, null, WatchService.class, null, Annotation.class, "services", "service");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Filter> createFilterClassFilter() {
|
||||
return createClassFilter(null, null, WatchFilter.class, null, null, "filters", "filter");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Servlet> createServletClassFilter() {
|
||||
return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet");
|
||||
}
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
*/
|
||||
package org.redkale.boot.watch;
|
||||
|
||||
import org.redkale.net.http.RestService;
|
||||
import java.util.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.*;
|
||||
import org.redkale.net.Server;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.service.RetResult;
|
||||
import org.redkale.util.Comment;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -14,4 +20,66 @@ import org.redkale.net.http.RestService;
|
||||
@RestService(name = "server", catalog = "watch", repair = false)
|
||||
public class ServerWatchService extends AbstractWatchService {
|
||||
|
||||
@Comment("不存在的Server节点")
|
||||
public static final int RET_SERVER_NOT_EXISTS = 1602_0001;
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
|
||||
@RestMapping(name = "info", comment = "单个Server信息查询")
|
||||
public RetResult info(@RestParam(name = "#port:") int port) {
|
||||
NodeServer node = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
if (ns.getServer().getSocketAddress().getPort() == port) {
|
||||
node = ns;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + port + ") not found");
|
||||
return new RetResult(formatToMap(node));
|
||||
}
|
||||
|
||||
@RestMapping(name = "infos", comment = "Server信息查询")
|
||||
public RetResult infos() {
|
||||
Map<String, Object> rs = new LinkedHashMap<>();
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
Server server = ns.getServer();
|
||||
rs.put("" + server.getSocketAddress().getPort(), formatToMap(ns));
|
||||
}
|
||||
return new RetResult(rs);
|
||||
}
|
||||
|
||||
private Map<String, Object> formatToMap(NodeServer node) {
|
||||
Server server = node.getServer();
|
||||
Map<String, Object> rs = new LinkedHashMap<>();
|
||||
String protocol = server.getProtocol();
|
||||
if (node instanceof NodeSncpServer) {
|
||||
protocol += "/SNCP";
|
||||
} else if (node instanceof NodeWatchServer) {
|
||||
protocol += "/WATCH";
|
||||
} else if (node instanceof NodeHttpServer) {
|
||||
protocol += "/HTTP";
|
||||
} else {
|
||||
NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class);
|
||||
if (np != null && np.value().length > 0) protocol += "/" + np.value()[0];
|
||||
}
|
||||
rs.put("name", server.getName());
|
||||
rs.put("protocol", protocol);
|
||||
rs.put("address", server.getSocketAddress());
|
||||
rs.put("threads", server.getThreads());
|
||||
rs.put("backlog", server.getBacklog());
|
||||
rs.put("bufferCapacity", server.getBufferCapacity());
|
||||
rs.put("bufferPoolSize", server.getBufferPoolSize());
|
||||
rs.put("charset", server.getCharset() == null ? "UTF-8" : server.getCharset().name());
|
||||
rs.put("maxbody", server.getMaxbody());
|
||||
rs.put("maxconns", server.getMaxconns());
|
||||
rs.put("serverStartTime", server.getServerStartTime());
|
||||
rs.put("responsePoolSize", server.getResponsePoolSize());
|
||||
rs.put("readTimeoutSeconds", server.getReadTimeoutSeconds());
|
||||
rs.put("writeTimeoutSeconds", server.getWriteTimeoutSeconds());
|
||||
rs.put("createConnectionCount", server.getCreateConnectionCount());
|
||||
rs.put("livingConnectionCount", server.getLivingConnectionCount());
|
||||
rs.put("closedConnectionCount", server.getClosedConnectionCount());
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|
||||
private final Set<Class> skipIgnores = new HashSet();
|
||||
|
||||
//key:需要屏蔽的字段;value:排除的字段名
|
||||
private final ConcurrentHashMap<Class, Set<String>> ignoreAlls = new ConcurrentHashMap();
|
||||
|
||||
private boolean skipAllIgnore = false;
|
||||
|
||||
protected ConvertFactory(ConvertFactory<R, W> parent, boolean tiny) {
|
||||
@@ -171,12 +174,19 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
public ConvertColumnEntry findRef(AccessibleObject element) {
|
||||
if (element == null) return null;
|
||||
ConvertColumnEntry en = this.columnEntrys.get(element);
|
||||
if (en != null) return en;
|
||||
Set<String> onlyColumns = null;
|
||||
if (element instanceof Method) {
|
||||
onlyColumns = ignoreAlls.get(((Method) element).getDeclaringClass());
|
||||
} else if (element instanceof Field) {
|
||||
onlyColumns = ignoreAlls.get(((Field) element).getDeclaringClass());
|
||||
}
|
||||
if (en != null && onlyColumns == null) return en;
|
||||
final ConvertType ct = this.getConvertType();
|
||||
ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class);
|
||||
String fieldName = null;
|
||||
if (ccs.length == 0 && element instanceof Method) {
|
||||
final Method method = (Method) element;
|
||||
String fieldName = readGetSetFieldName(method);
|
||||
fieldName = readGetSetFieldName(method);
|
||||
if (fieldName != null) {
|
||||
try {
|
||||
ccs = method.getDeclaringClass().getDeclaredField(fieldName).getAnnotationsByType(ConvertColumn.class);
|
||||
@@ -184,8 +194,22 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (onlyColumns != null && fieldName == null) {
|
||||
if (element instanceof Method) {
|
||||
fieldName = readGetSetFieldName((Method) element);
|
||||
} else if (element instanceof Field) {
|
||||
fieldName = ((Field) element).getName();
|
||||
}
|
||||
}
|
||||
if (ccs.length == 0 && onlyColumns != null && fieldName != null) {
|
||||
if (!onlyColumns.contains(fieldName)) return new ConvertColumnEntry(fieldName, true);
|
||||
}
|
||||
for (ConvertColumn ref : ccs) {
|
||||
if (ref.type().contains(ct)) {
|
||||
if (onlyColumns != null && fieldName != null) {
|
||||
String realName = ref.name().isEmpty() ? fieldName : ref.name();
|
||||
if (!onlyColumns.contains(realName)) return new ConvertColumnEntry(realName, true);
|
||||
}
|
||||
ConvertColumnEntry entry = new ConvertColumnEntry(ref);
|
||||
if (skipAllIgnore) {
|
||||
entry.setIgnore(false);
|
||||
@@ -318,6 +342,22 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
skipIgnores.add(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 屏蔽指定类所有字段,仅仅保留指定字段 <br>
|
||||
* <b>注意: 该配置优先级高于skipAllIgnore和ConvertColumnEntry配置</b>
|
||||
*
|
||||
* @param type 指定的类
|
||||
* @param excludeColumns 需要排除的字段名
|
||||
*/
|
||||
public final void registerIgnoreAll(final Class type, String... excludeColumns) {
|
||||
Set<String> set = ignoreAlls.get(type);
|
||||
if (set == null) {
|
||||
ignoreAlls.put(type, new HashSet<>(Arrays.asList(excludeColumns)));
|
||||
} else {
|
||||
set.addAll(Arrays.asList(excludeColumns));
|
||||
}
|
||||
}
|
||||
|
||||
public final void register(final Class type, boolean ignore, String... columns) {
|
||||
for (String column : columns) {
|
||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
||||
|
||||
@@ -34,6 +34,7 @@ public class OptionalCoder<R extends Reader, W extends Writer, T> extends Simple
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public OptionalCoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
try {
|
||||
|
||||
@@ -25,6 +25,7 @@ public final class BigIntegerSimpledCoder<R extends Reader, W extends Writer> ex
|
||||
public static final BigIntegerSimpledCoder instance = new BigIntegerSimpledCoder();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void convertTo(W out, BigInteger value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
@@ -34,6 +35,7 @@ public final class BigIntegerSimpledCoder<R extends Reader, W extends Writer> ex
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public BigInteger convertFrom(R in) {
|
||||
byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in);
|
||||
return bytes == null ? null : new BigInteger(bytes);
|
||||
|
||||
@@ -27,6 +27,7 @@ public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
public static final DLongSimpledCoder instance = new DLongSimpledCoder();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void convertTo(final W out, final DLong value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
@@ -36,6 +37,7 @@ public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DLong convertFrom(R in) {
|
||||
byte[] bs = bsSimpledCoder.convertFrom(in);
|
||||
if (bs == null) return null;
|
||||
|
||||
@@ -74,6 +74,7 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
public static final DoubleStreamSimpledCoder instance = new DoubleStreamSimpledCoder();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void convertTo(W out, DoubleStream values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
@@ -83,6 +84,7 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DoubleStream convertFrom(R in) {
|
||||
double[] value = DoubleArraySimpledCoder.instance.convertFrom(in);
|
||||
return value == null ? null : DoubleStream.of(value);
|
||||
|
||||
@@ -74,6 +74,7 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
|
||||
public static final IntStreamSimpledCoder instance = new IntStreamSimpledCoder();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void convertTo(W out, IntStream values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
@@ -83,6 +84,7 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public IntStream convertFrom(R in) {
|
||||
int[] value = IntArraySimpledCoder.instance.convertFrom(in);
|
||||
return value == null ? null : IntStream.of(value);
|
||||
|
||||
@@ -74,6 +74,7 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
public static final LongStreamSimpledCoder instance = new LongStreamSimpledCoder();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void convertTo(W out, LongStream values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
@@ -83,6 +84,7 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public LongStream convertFrom(R in) {
|
||||
long[] value = LongArraySimpledCoder.instance.convertFrom(in);
|
||||
return value == null ? null : LongStream.of(value);
|
||||
|
||||
@@ -22,8 +22,6 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public class JsonByteBufferWriter extends JsonWriter {
|
||||
|
||||
protected static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
protected Charset charset;
|
||||
|
||||
private final Supplier<ByteBuffer> supplier;
|
||||
@@ -38,7 +36,7 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
|
||||
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) {
|
||||
this.tiny = tiny;
|
||||
this.charset = UTF8.equals(charset) ? null : charset;
|
||||
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
@@ -33,11 +34,13 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
protected volatile long writetime;
|
||||
|
||||
//在线数
|
||||
protected AtomicLong livingCounter;
|
||||
|
||||
//关闭数
|
||||
protected AtomicLong closedCounter;
|
||||
|
||||
//在线数
|
||||
protected AtomicLong livingCounter;
|
||||
protected Consumer<AsyncConnection> beforeCloseListener;
|
||||
|
||||
public final long getLastReadTime() {
|
||||
return readtime;
|
||||
@@ -49,17 +52,39 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
public abstract boolean isTCP();
|
||||
|
||||
public abstract boolean shutdownInput();
|
||||
|
||||
public abstract boolean shutdownOutput();
|
||||
|
||||
public abstract <T> boolean setOption(SocketOption<T> name, T value);
|
||||
|
||||
public abstract Set<SocketOption<?>> supportedOptions();
|
||||
|
||||
public abstract SocketAddress getRemoteAddress();
|
||||
|
||||
public abstract SocketAddress getLocalAddress();
|
||||
|
||||
public abstract int getReadTimeoutSecond();
|
||||
public abstract int getReadTimeoutSeconds();
|
||||
|
||||
public abstract int getWriteTimeoutSecond();
|
||||
public abstract int getWriteTimeoutSeconds();
|
||||
|
||||
public abstract void setReadTimeoutSecond(int readTimeoutSecond);
|
||||
public abstract void setReadTimeoutSeconds(int readTimeoutSeconds);
|
||||
|
||||
public abstract void setWriteTimeoutSecond(int writeTimeoutSecond);
|
||||
public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds);
|
||||
|
||||
@Override
|
||||
public abstract Future<Integer> read(ByteBuffer dst);
|
||||
|
||||
@Override
|
||||
public abstract <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
|
||||
public abstract <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
|
||||
@Override
|
||||
public abstract Future<Integer> write(ByteBuffer src);
|
||||
|
||||
@Override
|
||||
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
|
||||
public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
write(srcs, 0, srcs.length, attachment, handler);
|
||||
@@ -74,6 +99,11 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncConnection beforeCloseListener(Consumer<AsyncConnection> beforeCloseListener) {
|
||||
this.beforeCloseListener = beforeCloseListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (closedCounter != null) {
|
||||
@@ -84,6 +114,11 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
livingCounter.decrementAndGet();
|
||||
livingCounter = null;
|
||||
}
|
||||
if (beforeCloseListener != null)
|
||||
try {
|
||||
beforeCloseListener.accept(this);
|
||||
} catch (Exception io) {
|
||||
}
|
||||
if (attributes == null) return;
|
||||
try {
|
||||
for (Object obj : attributes.values()) {
|
||||
@@ -130,14 +165,14 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
*
|
||||
* @param address 连接点子
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSecond 读取超时秒数
|
||||
* @param writeTimeoutSecond 写入超时秒数
|
||||
* @param readTimeoutSeconds 读取超时秒数
|
||||
* @param writeTimeoutSeconds 写入超时秒数
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
|
||||
final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
return createTCP(group, null, address, false, readTimeoutSecond, writeTimeoutSecond);
|
||||
final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return createTCP(group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,43 +181,26 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
* @param address 连接点子
|
||||
* @param sslContext SSLContext
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSecond 读取超时秒数
|
||||
* @param writeTimeoutSecond 写入超时秒数
|
||||
* @param readTimeoutSeconds 读取超时秒数
|
||||
* @param writeTimeoutSeconds 写入超时秒数
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SSLContext sslContext,
|
||||
final SocketAddress address, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
return createTCP(group, sslContext, address, false, readTimeoutSecond, writeTimeoutSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param address 连接点子
|
||||
* @param sslContext SSLContext
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param noDelay TcpNoDelay
|
||||
* @param readTimeoutSecond 读取超时秒数
|
||||
* @param writeTimeoutSecond 写入超时秒数
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SSLContext sslContext,
|
||||
final SocketAddress address, final boolean noDelay, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
final CompletableFuture future = new CompletableFuture();
|
||||
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
|
||||
try {
|
||||
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||
try {
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
channel.connect(address, null, new CompletionHandler<Void, Void>() {
|
||||
@Override
|
||||
public void completed(Void result, Void attachment) {
|
||||
if (noDelay) {
|
||||
try {
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
future.complete(create(channel, sslContext, address, readTimeoutSecond, writeTimeoutSecond));
|
||||
future.complete(create(channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -196,287 +214,6 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
return future;
|
||||
}
|
||||
|
||||
private static class BIOUDPAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSecond;
|
||||
|
||||
private int writeTimeoutSecond;
|
||||
|
||||
private final DatagramChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
private final boolean client;
|
||||
|
||||
public BIOUDPAsyncConnection(final DatagramChannel ch, SocketAddress addr,
|
||||
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
this.channel = ch;
|
||||
this.client = client0;
|
||||
this.readTimeoutSecond = readTimeoutSecond0;
|
||||
this.writeTimeoutSecond = writeTimeoutSecond0;
|
||||
this.remoteAddress = addr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSecond(int readTimeoutSecond) {
|
||||
this.readTimeoutSecond = readTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
|
||||
this.writeTimeoutSecond = writeTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSecond() {
|
||||
return this.readTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSecond() {
|
||||
return this.writeTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
try {
|
||||
return channel.getLocalAddress();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
rs += channel.send(srcs[i], remoteAddress);
|
||||
if (i != offset) Thread.sleep(10);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (Exception e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
if (client) channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTCP() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
|
||||
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
return new BIOUDPAsyncConnection(ch, addr, client0, readTimeoutSecond0, writeTimeoutSecond0);
|
||||
}
|
||||
|
||||
private static class BIOTCPAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSecond;
|
||||
|
||||
private int writeTimeoutSecond;
|
||||
|
||||
private final Socket socket;
|
||||
|
||||
private final ReadableByteChannel readChannel;
|
||||
|
||||
private final WritableByteChannel writeChannel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
public BIOTCPAsyncConnection(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
this.socket = socket;
|
||||
ReadableByteChannel rc = null;
|
||||
WritableByteChannel wc = null;
|
||||
try {
|
||||
socket.setSoTimeout(Math.max(readTimeoutSecond0, writeTimeoutSecond0));
|
||||
rc = Channels.newChannel(socket.getInputStream());
|
||||
wc = Channels.newChannel(socket.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.readChannel = rc;
|
||||
this.writeChannel = wc;
|
||||
this.readTimeoutSecond = readTimeoutSecond0;
|
||||
this.writeTimeoutSecond = writeTimeoutSecond0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = socket.getRemoteSocketAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
return socket.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSecond() {
|
||||
return readTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSecond() {
|
||||
return writeTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSecond(int readTimeoutSecond) {
|
||||
this.readTimeoutSecond = readTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
|
||||
this.writeTimeoutSecond = writeTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
rs += writeChannel.write(srcs[i]);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return !socket.isClosed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通常用于 ssl socket
|
||||
*
|
||||
@@ -489,150 +226,68 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
return new BIOTCPAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0);
|
||||
return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
|
||||
}
|
||||
|
||||
private static class AIOTCPAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSecond;
|
||||
|
||||
private int writeTimeoutSecond;
|
||||
|
||||
private final AsynchronousSocketChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
public AIOTCPAsyncConnection(final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
this.channel = ch;
|
||||
this.sslContext = sslContext;
|
||||
this.readTimeoutSecond = readTimeoutSecond0;
|
||||
this.writeTimeoutSecond = writeTimeoutSecond0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = ch.getRemoteAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0,
|
||||
final int writeTimeoutSecond0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (readTimeoutSecond > 0) {
|
||||
channel.read(dst, readTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
channel.read(dst, attachment, handler);
|
||||
}
|
||||
public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
|
||||
return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (writeTimeoutSecond > 0) {
|
||||
channel.write(src, writeTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
channel.write(src, attachment, handler);
|
||||
}
|
||||
public static AsyncConnection create(final SocketChannel ch, final SocketAddress addr0, final Selector selector, final Context context) {
|
||||
return new TcpNioAsyncConnection(ch, addr0, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
|
||||
this.writetime = System.currentTimeMillis();
|
||||
channel.write(srcs, offset, length, writeTimeoutSecond > 0 ? writeTimeoutSecond : 60, TimeUnit.SECONDS,
|
||||
attachment, new CompletionHandler<Long, A>() {
|
||||
|
||||
@Override
|
||||
public void completed(Long result, A attachment) {
|
||||
handler.completed(result.intValue(), attachment);
|
||||
public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
handler.failed(exc, attachment);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSecond(int readTimeoutSecond) {
|
||||
this.readTimeoutSecond = readTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
|
||||
this.writeTimeoutSecond = writeTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSecond() {
|
||||
return this.readTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSecond() {
|
||||
return this.writeTimeoutSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
try {
|
||||
return channel.getLocalAddress();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Future<Integer> read(ByteBuffer dst) {
|
||||
return channel.read(dst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Future<Integer> write(ByteBuffer src) {
|
||||
return channel.write(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTCP() {
|
||||
return true;
|
||||
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
|
||||
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
|
||||
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
|
||||
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch) {
|
||||
return create(ch, null, 0, 0);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
return new AIOTCPAsyncConnection(ch, null, addr0, readTimeoutSecond, writeTimeoutSecond);
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
return new AIOTCPAsyncConnection(ch, sslContext, addr0, readTimeoutSecond, writeTimeoutSecond);
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final Context context) {
|
||||
return new AIOTCPAsyncConnection(ch, context.sslContext, addr0, context.readTimeoutSecond, context.writeTimeoutSecond);
|
||||
return new TcpAioAsyncConnection(ch, context.sslContext, addr0, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSeconds,
|
||||
final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSeconds,
|
||||
final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0,
|
||||
final Context context, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(ch, context.sslContext, addr0, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public class Context {
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
//服务启动时间
|
||||
protected final long serverStartTime;
|
||||
|
||||
@@ -57,14 +55,20 @@ public class Context {
|
||||
//字符集
|
||||
protected final Charset charset;
|
||||
|
||||
//最大连接数, 为0表示没限制
|
||||
protected final int maxconns;
|
||||
|
||||
//请求内容的大小上限, 默认64K
|
||||
protected final int maxbody;
|
||||
|
||||
//keep alive IO读取的超时时间
|
||||
protected final int aliveTimeoutSeconds;
|
||||
|
||||
//IO读取的超时时间
|
||||
protected final int readTimeoutSecond;
|
||||
protected final int readTimeoutSeconds;
|
||||
|
||||
//IO写入的超时时间
|
||||
protected final int writeTimeoutSecond;
|
||||
protected final int writeTimeoutSeconds;
|
||||
|
||||
//日志Logger
|
||||
protected final Logger logger;
|
||||
@@ -78,10 +82,17 @@ public class Context {
|
||||
//依赖注入工厂类
|
||||
protected final ResourceFactory resourceFactory;
|
||||
|
||||
public Context(ContextConfig config) {
|
||||
this(config.serverStartTime, config.logger, config.executor, config.sslContext,
|
||||
config.bufferCapacity, config.bufferPool, config.responsePool, config.maxconns, config.maxbody,
|
||||
config.charset, config.address, config.resourceFactory, config.prepare,
|
||||
config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
public Context(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext,
|
||||
int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
|
||||
int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, final int maxconns,
|
||||
final int maxbody, Charset charset, InetSocketAddress address, ResourceFactory resourceFactory,
|
||||
final PrepareServlet prepare, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
final PrepareServlet prepare, final int aliveTimeoutSeconds, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
this.serverStartTime = serverStartTime;
|
||||
this.logger = logger;
|
||||
this.executor = executor;
|
||||
@@ -89,13 +100,15 @@ public class Context {
|
||||
this.bufferCapacity = bufferCapacity;
|
||||
this.bufferPool = bufferPool;
|
||||
this.responsePool = responsePool;
|
||||
this.maxconns = maxconns;
|
||||
this.maxbody = maxbody;
|
||||
this.charset = UTF8.equals(charset) ? null : charset;
|
||||
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
|
||||
this.address = address;
|
||||
this.prepare = prepare;
|
||||
this.resourceFactory = resourceFactory;
|
||||
this.readTimeoutSecond = readTimeoutSecond;
|
||||
this.writeTimeoutSecond = writeTimeoutSecond;
|
||||
this.aliveTimeoutSeconds = aliveTimeoutSeconds;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
this.jsonFactory = JsonFactory.root();
|
||||
this.bsonFactory = BsonFactory.root();
|
||||
}
|
||||
@@ -108,6 +121,10 @@ public class Context {
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
public int getMaxconns() {
|
||||
return maxconns;
|
||||
}
|
||||
|
||||
public int getMaxbody() {
|
||||
return maxbody;
|
||||
}
|
||||
@@ -144,15 +161,19 @@ public class Context {
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
protected Consumer<ByteBuffer> getBufferConsumer() {
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
public ByteBuffer pollBuffer() {
|
||||
return bufferPool.get();
|
||||
}
|
||||
|
||||
public void offerBuffer(ByteBuffer buffer) {
|
||||
protected void offerBuffer(ByteBuffer buffer) {
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
|
||||
public void offerBuffer(ByteBuffer... buffers) {
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
if (buffers == null) return;
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
bufferPool.accept(buffer);
|
||||
@@ -163,12 +184,16 @@ public class Context {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public int getReadTimeoutSecond() {
|
||||
return readTimeoutSecond;
|
||||
public int getAliveTimeoutSeconds() {
|
||||
return aliveTimeoutSeconds;
|
||||
}
|
||||
|
||||
public int getWriteTimeoutSecond() {
|
||||
return writeTimeoutSecond;
|
||||
public int getReadTimeoutSeconds() {
|
||||
return readTimeoutSeconds;
|
||||
}
|
||||
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
public JsonConvert getJsonConvert() {
|
||||
@@ -178,4 +203,60 @@ public class Context {
|
||||
public BsonConvert getBsonConvert() {
|
||||
return bsonFactory.getConvert();
|
||||
}
|
||||
|
||||
public static class ContextConfig {
|
||||
|
||||
//服务启动时间
|
||||
public long serverStartTime;
|
||||
|
||||
//Server的线程池
|
||||
public ThreadPoolExecutor executor;
|
||||
|
||||
//SSL
|
||||
public SSLContext sslContext;
|
||||
|
||||
//ByteBuffer的容量,默认8K
|
||||
public int bufferCapacity;
|
||||
|
||||
//ByteBuffer对象池
|
||||
public ObjectPool<ByteBuffer> bufferPool;
|
||||
|
||||
//Response对象池
|
||||
public ObjectPool<Response> responsePool;
|
||||
|
||||
//服务的根Servlet
|
||||
public PrepareServlet prepare;
|
||||
|
||||
//服务的监听地址
|
||||
public InetSocketAddress address;
|
||||
|
||||
//字符集
|
||||
public Charset charset;
|
||||
|
||||
//请求内容的大小上限, 默认64K
|
||||
public int maxbody;
|
||||
|
||||
//最大连接数, 为0表示没限制
|
||||
public int maxconns;
|
||||
|
||||
//keep alive IO读取的超时时间
|
||||
public int aliveTimeoutSeconds;
|
||||
|
||||
//IO读取的超时时间
|
||||
public int readTimeoutSeconds;
|
||||
|
||||
//IO写入的超时时间
|
||||
public int writeTimeoutSeconds;
|
||||
|
||||
//日志Logger
|
||||
public Logger logger;
|
||||
|
||||
//依赖注入工厂类
|
||||
public ResourceFactory resourceFactory;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
src/org/redkale/net/Cryptor.java
Normal file
42
src/org/redkale/net/Cryptor.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
* 加密解密接口
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface Cryptor {
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param buffers 待加密数据
|
||||
* @param supplier ByteBuffer生成器
|
||||
* @param consumer ByteBuffer回收器
|
||||
*
|
||||
* @return 加密后数据
|
||||
*/
|
||||
public ByteBuffer[] encrypt(ByteBuffer[] buffers, final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer);
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param buffers 待解密数据
|
||||
* @param supplier ByteBuffer生成器
|
||||
* @param consumer ByteBuffer回收器
|
||||
*
|
||||
* @return 解密后数据
|
||||
*/
|
||||
public ByteBuffer[] decrypt(ByteBuffer[] buffers, final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ package org.redkale.net;
|
||||
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -27,20 +28,24 @@ public final class PrepareRunner implements Runnable {
|
||||
|
||||
private ByteBuffer data;
|
||||
|
||||
public PrepareRunner(Context context, AsyncConnection channel, ByteBuffer data) {
|
||||
private Response response;
|
||||
|
||||
public PrepareRunner(Context context, AsyncConnection channel, ByteBuffer data, Response response) {
|
||||
this.context = context;
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean keepalive = response != null;
|
||||
final PrepareServlet prepare = context.prepare;
|
||||
final ObjectPool<? extends Response> responsePool = context.responsePool;
|
||||
if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
|
||||
final Response response = responsePool.get();
|
||||
response.init(channel);
|
||||
if (response == null) response = responsePool.get();
|
||||
try {
|
||||
response.init(channel);
|
||||
prepare.prepare(data, response.request, response);
|
||||
} catch (Throwable t) {
|
||||
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", t);
|
||||
@@ -48,20 +53,18 @@ public final class PrepareRunner implements Runnable {
|
||||
}
|
||||
return;
|
||||
}
|
||||
final Response response = responsePool.get();
|
||||
if (response == null) response = responsePool.get();
|
||||
final ByteBuffer buffer = response.request.pollReadBuffer();
|
||||
try {
|
||||
channel.read(buffer, null, new CompletionHandler<Integer, Void>() {
|
||||
channel.read(buffer, keepalive ? context.getAliveTimeoutSeconds() : 0, TimeUnit.SECONDS, null,
|
||||
new CompletionHandler<Integer, Void>() {
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment1) {
|
||||
if (count < 1 && buffer.remaining() == buffer.limit()) {
|
||||
try {
|
||||
if (count < 1) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
channel.dispose();// response.init(channel); 在调用之前异常
|
||||
response.removeChannel();
|
||||
response.finish(true);
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
context.logger.log(Level.FINEST, "PrepareRunner close channel erroneous on no read bytes", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// { //测试
|
||||
@@ -83,22 +86,22 @@ public final class PrepareRunner implements Runnable {
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
channel.dispose();// response.init(channel); 在调用之前异常
|
||||
response.removeChannel();
|
||||
response.finish(true);
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
|
||||
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, forece to close channel ", exc);
|
||||
}
|
||||
if (exc != null) context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, forece to close channel ", exc);
|
||||
}
|
||||
});
|
||||
} catch (Exception te) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
channel.dispose();// response.init(channel); 在调用之前异常
|
||||
response.removeChannel();
|
||||
response.finish(true);
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
if (te != null && context.logger.isLoggable(Level.FINEST)) {
|
||||
context.logger.log(Level.FINEST, "Servlet read channel erroneous, forece to close channel ", te);
|
||||
}
|
||||
if (te != null) context.logger.log(Level.FINEST, "Servlet read channel erroneous, forece to close channel ", te);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,9 @@ package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 协议底层Server
|
||||
@@ -32,10 +30,12 @@ public abstract class ProtocolServer {
|
||||
//在线数
|
||||
protected final AtomicLong livingCounter = new AtomicLong();
|
||||
|
||||
protected final Context context;
|
||||
|
||||
//最大连接数,小于1表示无限制
|
||||
protected int maxconns;
|
||||
|
||||
public abstract void open() throws IOException;
|
||||
public abstract void open(AnyValue config) throws IOException;
|
||||
|
||||
public abstract void bind(SocketAddress local, int backlog) throws IOException;
|
||||
|
||||
@@ -43,15 +43,14 @@ public abstract class ProtocolServer {
|
||||
|
||||
public abstract <T> void setOption(SocketOption<T> name, T value) throws IOException;
|
||||
|
||||
public abstract void accept();
|
||||
|
||||
public void setMaxconns(int maxconns) {
|
||||
this.maxconns = maxconns;
|
||||
}
|
||||
public abstract void accept() throws IOException;
|
||||
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
public abstract AsynchronousChannelGroup getChannelGroup();
|
||||
protected ProtocolServer(Context context) {
|
||||
this.context = context;
|
||||
this.maxconns = context.getMaxconns();
|
||||
}
|
||||
|
||||
public long getCreateCount() {
|
||||
return createCounter.longValue();
|
||||
@@ -66,176 +65,31 @@ public abstract class ProtocolServer {
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
public static ProtocolServer create(String protocol, Context context) {
|
||||
if ("TCP".equalsIgnoreCase(protocol)) return new ProtocolTCPServer(context);
|
||||
if ("UDP".equalsIgnoreCase(protocol)) return new ProtocolUDPServer(context);
|
||||
public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) {
|
||||
if (netimpl != null) netimpl = netimpl.trim();
|
||||
if ("TCP".equalsIgnoreCase(protocol)) {
|
||||
if (netimpl == null || netimpl.isEmpty()) {
|
||||
return new TcpAioProtocolServer(context);
|
||||
} else if ("aio".equalsIgnoreCase(netimpl)) {
|
||||
return new TcpAioProtocolServer(context);
|
||||
} else if ("nio".equalsIgnoreCase(netimpl)) {
|
||||
return new TcpNioProtocolServer(context);
|
||||
}
|
||||
} else if ("UDP".equalsIgnoreCase(protocol)) {
|
||||
if (netimpl == null || netimpl.isEmpty()) {
|
||||
return new UdpBioProtocolServer(context);
|
||||
} else if ("bio".equalsIgnoreCase(netimpl)) {
|
||||
return new UdpBioProtocolServer(context);
|
||||
}
|
||||
} else if (netimpl == null || netimpl.isEmpty()) {
|
||||
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
|
||||
}
|
||||
|
||||
private static final class ProtocolUDPServer extends ProtocolServer {
|
||||
|
||||
private boolean running;
|
||||
|
||||
private final Context context;
|
||||
|
||||
private DatagramChannel serverChannel;
|
||||
|
||||
public ProtocolUDPServer(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
DatagramChannel ch = DatagramChannel.open();
|
||||
ch.configureBlocking(true);
|
||||
this.serverChannel = ch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
this.serverChannel.bind(local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
this.serverChannel.setOption(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<SocketOption<?>> supportedOptions() {
|
||||
return this.serverChannel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() {
|
||||
final DatagramChannel serchannel = this.serverChannel;
|
||||
final int readTimeoutSecond = this.context.readTimeoutSecond;
|
||||
final int writeTimeoutSecond = this.context.writeTimeoutSecond;
|
||||
final CountDownLatch cdl = new CountDownLatch(1);
|
||||
this.running = true;
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
cdl.countDown();
|
||||
while (running) {
|
||||
final ByteBuffer buffer = context.pollBuffer();
|
||||
try {
|
||||
SocketAddress address = serchannel.receive(buffer);
|
||||
buffer.flip();
|
||||
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond);
|
||||
context.runAsync(new PrepareRunner(context, conn, buffer));
|
||||
if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Class clazz = classLoader.loadClass(netimpl);
|
||||
return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context);
|
||||
} catch (Exception e) {
|
||||
context.offerBuffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
cdl.await();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.running = false;
|
||||
this.serverChannel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousChannelGroup getChannelGroup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreateCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getClosedCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLivingCount() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProtocolTCPServer extends ProtocolServer {
|
||||
|
||||
private final Context context;
|
||||
|
||||
private AsynchronousChannelGroup group;
|
||||
|
||||
private AsynchronousServerSocketChannel serverChannel;
|
||||
|
||||
public ProtocolTCPServer(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
group = AsynchronousChannelGroup.withCachedThreadPool(context.executor, 1);
|
||||
this.serverChannel = AsynchronousServerSocketChannel.open(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
this.serverChannel.bind(local, backlog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
this.serverChannel.setOption(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<SocketOption<?>> supportedOptions() {
|
||||
return this.serverChannel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() {
|
||||
final AsynchronousServerSocketChannel serchannel = this.serverChannel;
|
||||
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
|
||||
|
||||
@Override
|
||||
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
|
||||
serchannel.accept(null, this);
|
||||
if (maxconns > 0 && livingCounter.get() >= maxconns) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
createCounter.incrementAndGet();
|
||||
livingCounter.incrementAndGet();
|
||||
AsyncConnection conn = AsyncConnection.create(channel, null, context);
|
||||
conn.livingCounter = livingCounter;
|
||||
conn.closedCounter = closedCounter;
|
||||
context.runAsync(new PrepareRunner(context, conn, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
serchannel.accept(null, this);
|
||||
//if (exc != null) context.logger.log(Level.FINEST, AsynchronousServerSocketChannel.class.getSimpleName() + " accept erroneous", exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.serverChannel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousChannelGroup getChannelGroup() {
|
||||
return this.group;
|
||||
throw new RuntimeException("ProtocolServer(netimple=" + netimpl + ") newinstance error", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,10 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
return bodyBufferSupplier;
|
||||
}
|
||||
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
context.offerBuffer(buffers);
|
||||
}
|
||||
|
||||
protected AsyncConnection removeChannel() {
|
||||
AsyncConnection ch = this.channel;
|
||||
this.channel = null;
|
||||
@@ -169,28 +173,12 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
|
||||
protected boolean recycle() {
|
||||
if (!inited) return false;
|
||||
boolean keepAlive = request.keepAlive;
|
||||
if (recycleListener != null) {
|
||||
try {
|
||||
recycleListener.accept(request, this);
|
||||
} catch (Exception e) {
|
||||
context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e);
|
||||
}
|
||||
recycleListener = null;
|
||||
}
|
||||
this.output = null;
|
||||
this.filter = null;
|
||||
this.servlet = null;
|
||||
request.recycle();
|
||||
if (channel != null) {
|
||||
if (keepAlive) {
|
||||
this.context.runAsync(new PrepareRunner(context, channel, null));
|
||||
} else {
|
||||
try {
|
||||
if (channel.isOpen()) channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
channel.dispose();
|
||||
channel = null;
|
||||
}
|
||||
this.inited = false;
|
||||
@@ -255,8 +243,27 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
|
||||
if (kill) refuseAlive();
|
||||
if (this.recycleListener != null) {
|
||||
try {
|
||||
this.recycleListener.accept(request, this);
|
||||
} catch (Exception e) {
|
||||
context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e);
|
||||
}
|
||||
this.recycleListener = null;
|
||||
}
|
||||
if (request.keepAlive && channel != null) {
|
||||
if (channel.isOpen()) {
|
||||
AsyncConnection conn = removeChannel();
|
||||
this.recycle();
|
||||
this.prepare();
|
||||
new PrepareRunner(context, conn, null, this).run();
|
||||
} else {
|
||||
channel.dispose();
|
||||
}
|
||||
} else {
|
||||
this.context.responsePool.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void finish(final byte[] bs) {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
|
||||
20
src/org/redkale/net/SSLClientAuth.java
Normal file
20
src/org/redkale/net/SSLClientAuth.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public enum SSLClientAuth {
|
||||
NONE,
|
||||
NEED,
|
||||
WANT,
|
||||
CLIENT;
|
||||
}
|
||||
65
src/org/redkale/net/SSLCreator.java
Normal file
65
src/org/redkale/net/SSLCreator.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import javax.net.ssl.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 根据配置生成SSLContext
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface SSLCreator {
|
||||
|
||||
default SSLContext create(Server server, AnyValue sslConf) throws Exception {
|
||||
String keyfile = sslConf.getValue("keystorefile");
|
||||
String keypass = sslConf.getValue("keystorepass", "");
|
||||
KeyManager[] keyManagers = null;
|
||||
if (keyfile != null) {
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(new FileInputStream(keyfile), keypass.toCharArray());
|
||||
kmf.init(ks, keypass.toCharArray());
|
||||
keyManagers = kmf.getKeyManagers();
|
||||
}
|
||||
|
||||
String trustfile = sslConf.getValue("truststorefile");
|
||||
String trustpass = sslConf.getValue("truststorepass", "");
|
||||
TrustManager[] trustManagers;
|
||||
if (trustfile != null) {
|
||||
KeyStore ts = KeyStore.getInstance("JKS");
|
||||
ts.load(new FileInputStream(trustfile), trustpass.toCharArray());
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||
tmf.init(ts);
|
||||
trustManagers = tmf.getTrustManagers();
|
||||
} else {
|
||||
trustManagers = new TrustManager[]{new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
}};
|
||||
}
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(keyManagers, trustManagers, new SecureRandom());
|
||||
return sslContext;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
|
||||
public static final String RESNAME_SERVER_EXECUTOR = "SERVER_EXECUTOR";
|
||||
|
||||
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
//-------------------------------------------------------------
|
||||
@@ -52,6 +54,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
//服务的根Servlet
|
||||
protected final PrepareServlet<K, C, R, P, S> prepare;
|
||||
|
||||
//ClassLoader
|
||||
protected RedkaleClassLoader serverClassLoader;
|
||||
|
||||
//SSL
|
||||
protected SSLContext sslContext;
|
||||
|
||||
@@ -88,17 +93,20 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
//Response池大小
|
||||
protected int responsePoolSize;
|
||||
|
||||
//最大连接数, 为0表示没限制
|
||||
protected int maxconns;
|
||||
|
||||
//请求包大小的上限,单位:字节
|
||||
protected int maxbody;
|
||||
|
||||
//Keep-Alive IO读取的超时秒数,小于1视为不设置
|
||||
protected int aliveTimeoutSeconds;
|
||||
|
||||
//IO读取的超时秒数,小于1视为不设置
|
||||
protected int readTimeoutSecond;
|
||||
protected int readTimeoutSeconds;
|
||||
|
||||
//IO写入 的超时秒数,小于1视为不设置
|
||||
protected int writeTimeoutSecond;
|
||||
|
||||
//最大连接数
|
||||
protected int maxconns;
|
||||
protected int writeTimeoutSeconds;
|
||||
|
||||
protected Server(long serverStartTime, String protocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) {
|
||||
this.serverStartTime = serverStartTime;
|
||||
@@ -113,17 +121,32 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80));
|
||||
this.charset = Charset.forName(config.getValue("charset", "UTF-8"));
|
||||
this.maxconns = config.getIntValue("maxconns", 0);
|
||||
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0);
|
||||
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0);
|
||||
this.backlog = parseLenth(config.getValue("backlog"), 16 * 1024);
|
||||
this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30);
|
||||
this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0);
|
||||
this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
|
||||
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
|
||||
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
|
||||
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 32 * 1024);
|
||||
this.bufferCapacity = bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity;
|
||||
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 8);
|
||||
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 32);
|
||||
this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
|
||||
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
|
||||
this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort());
|
||||
if (!this.name.matches("^[a-zA-Z][\\w_-]{1,64}$")) throw new RuntimeException("server.name (" + this.name + ") is illegal");
|
||||
AnyValue sslConf = config.getAnyValue("ssl");
|
||||
if (sslConf != null) {
|
||||
String creatorClass = sslConf.getValue("creator", SSLCreator.class.getName());
|
||||
SSLCreator creator = null;
|
||||
if (SSLCreator.class.getName().equals(creatorClass) || creatorClass.isEmpty()) {
|
||||
creator = new SSLCreator() {
|
||||
};
|
||||
} else {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
creator = ((SSLCreator) classLoader.loadClass(creatorClass).getDeclaredConstructor().newInstance());
|
||||
}
|
||||
this.resourceFactory.inject(creator);
|
||||
this.sslContext = creator.create(this, sslConf);
|
||||
}
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
final Format f = createFormat();
|
||||
final String n = name;
|
||||
@@ -135,12 +158,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
}
|
||||
|
||||
protected static int parseLenth(String value, int defValue) {
|
||||
if (value == null) return defValue;
|
||||
value = value.toUpperCase().replace("B", "");
|
||||
if (value.endsWith("G")) return Integer.decode(value.replace("G", "")) * 1024 * 1024 * 1024;
|
||||
if (value.endsWith("M")) return Integer.decode(value.replace("M", "")) * 1024 * 1024;
|
||||
if (value.endsWith("K")) return Integer.decode(value.replace("K", "")) * 1024;
|
||||
return Integer.decode(value);
|
||||
return (int) parseLenth(value, defValue + 0L);
|
||||
}
|
||||
|
||||
protected static long parseLenth(String value, long defValue) {
|
||||
@@ -152,6 +170,14 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
return Long.decode(value);
|
||||
}
|
||||
|
||||
protected static String formatLenth(long value) {
|
||||
if (value < 1) return "" + value;
|
||||
if (value % (1024 * 1024 * 1024) == 0) return value / (1024 * 1024 * 1024) + "G";
|
||||
if (value % (1024 * 1024) == 0) return value / (1024 * 1024) + "M";
|
||||
if (value % 1024 == 0) return value / (1024) + "K";
|
||||
return value + "B";
|
||||
}
|
||||
|
||||
public void destroy(final AnyValue config) throws Exception {
|
||||
this.prepare.destroy(context, config);
|
||||
}
|
||||
@@ -188,6 +214,54 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
return this.context;
|
||||
}
|
||||
|
||||
public long getServerStartTime() {
|
||||
return serverStartTime;
|
||||
}
|
||||
|
||||
public Charset getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public int getBacklog() {
|
||||
return backlog;
|
||||
}
|
||||
|
||||
public int getBufferCapacity() {
|
||||
return bufferCapacity;
|
||||
}
|
||||
|
||||
public int getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public int getBufferPoolSize() {
|
||||
return bufferPoolSize;
|
||||
}
|
||||
|
||||
public int getResponsePoolSize() {
|
||||
return responsePoolSize;
|
||||
}
|
||||
|
||||
public int getMaxbody() {
|
||||
return maxbody;
|
||||
}
|
||||
|
||||
public int getAliveTimeoutSeconds() {
|
||||
return aliveTimeoutSeconds;
|
||||
}
|
||||
|
||||
public int getReadTimeoutSeconds() {
|
||||
return readTimeoutSeconds;
|
||||
}
|
||||
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
public int getMaxconns() {
|
||||
return maxconns;
|
||||
}
|
||||
|
||||
public void setThreads(int threads) {
|
||||
int oldthreads = this.threads;
|
||||
this.context.executor.setCorePoolSize(threads);
|
||||
@@ -203,17 +277,13 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
public void start() throws IOException {
|
||||
this.context = this.createContext();
|
||||
this.prepare.init(this.context, config);
|
||||
this.serverChannel = ProtocolServer.create(this.protocol, context);
|
||||
this.serverChannel.open();
|
||||
if (this.serverChannel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
this.serverChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
|
||||
this.serverChannel.open(config);
|
||||
serverChannel.bind(address, backlog);
|
||||
serverChannel.setMaxconns(this.maxconns);
|
||||
serverChannel.accept();
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
|
||||
+ ", threads: " + threads + ", bufferCapacity: " + bufferCapacity / 1024 + "K, bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
|
||||
+ ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
|
||||
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
|
||||
}
|
||||
|
||||
@@ -232,6 +302,14 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
|
||||
}
|
||||
|
||||
public RedkaleClassLoader getServerClassLoader() {
|
||||
return serverClassLoader;
|
||||
}
|
||||
|
||||
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
|
||||
this.serverClassLoader = serverClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在Filter
|
||||
*
|
||||
|
||||
194
src/org/redkale/net/TcpAioAsyncConnection.java
Normal file
194
src/org/redkale/net/TcpAioAsyncConnection.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
|
||||
private final AsynchronousSocketChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
public TcpAioAsyncConnection(final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
this.channel = ch;
|
||||
this.sslContext = sslContext;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = ch.getRemoteAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
this.livingCounter = livingCounter;
|
||||
this.closedCounter = closedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownInput() {
|
||||
try {
|
||||
this.channel.shutdownInput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownOutput() {
|
||||
try {
|
||||
this.channel.shutdownOutput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(SocketOption<T> name, T value) {
|
||||
try {
|
||||
this.channel.setOption(name, value);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return this.channel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (readTimeoutSeconds > 0) {
|
||||
channel.read(dst, readTimeoutSeconds, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
channel.read(dst, attachment, handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.readtime = System.currentTimeMillis();
|
||||
channel.read(dst, timeout < 0 ? 0 : timeout, unit, attachment, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (writeTimeoutSeconds > 0) {
|
||||
channel.write(src, writeTimeoutSeconds, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
channel.write(src, attachment, handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
|
||||
this.writetime = System.currentTimeMillis();
|
||||
channel.write(srcs, offset, length, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS,
|
||||
attachment, new CompletionHandler<Long, A>() {
|
||||
|
||||
@Override
|
||||
public void completed(Long result, A attachment) {
|
||||
handler.completed(result.intValue(), attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
handler.failed(exc, attachment);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSeconds() {
|
||||
return this.readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return this.writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
try {
|
||||
return channel.getLocalAddress();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Future<Integer> read(ByteBuffer dst) {
|
||||
return channel.read(dst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Future<Integer> write(ByteBuffer src) {
|
||||
return channel.write(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
116
src/org/redkale/net/TcpAioProtocolServer.java
Normal file
116
src/org/redkale/net/TcpAioProtocolServer.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 协议底层Server
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpAioProtocolServer extends ProtocolServer {
|
||||
|
||||
private AsynchronousChannelGroup group;
|
||||
|
||||
private AsynchronousServerSocketChannel serverChannel;
|
||||
|
||||
public TcpAioProtocolServer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(AnyValue config) throws IOException {
|
||||
group = AsynchronousChannelGroup.withThreadPool(context.executor);
|
||||
this.serverChannel = AsynchronousServerSocketChannel.open(group);
|
||||
|
||||
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
|
||||
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
this.serverChannel.bind(local, backlog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
this.serverChannel.setOption(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<SocketOption<?>> supportedOptions() {
|
||||
return this.serverChannel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() throws IOException {
|
||||
final AsynchronousServerSocketChannel serchannel = this.serverChannel;
|
||||
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
|
||||
|
||||
@Override
|
||||
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
|
||||
serchannel.accept(null, this);
|
||||
if (maxconns > 0 && livingCounter.get() >= maxconns) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
createCounter.incrementAndGet();
|
||||
livingCounter.incrementAndGet();
|
||||
try {
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
} catch (IOException e) {
|
||||
context.logger.log(Level.INFO, channel + " setOption error", e);
|
||||
}
|
||||
AsyncConnection conn = new TcpAioAsyncConnection(channel, context.sslContext, null, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
conn.livingCounter = livingCounter;
|
||||
conn.closedCounter = closedCounter;
|
||||
context.runAsync(new PrepareRunner(context, conn, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
serchannel.accept(null, this);
|
||||
//if (exc != null) context.logger.log(Level.FINEST, AsynchronousServerSocketChannel.class.getSimpleName() + " accept erroneous", exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.serverChannel.close();
|
||||
}
|
||||
|
||||
}
|
||||
240
src/org/redkale/net/TcpBioAsyncConnection.java
Normal file
240
src/org/redkale/net/TcpBioAsyncConnection.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpBioAsyncConnection extends AsyncConnection {
|
||||
|
||||
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
|
||||
|
||||
private static Set<SocketOption<?>> defaultOptions() {
|
||||
HashSet<SocketOption<?>> set = new HashSet<>(5);
|
||||
set.add(StandardSocketOptions.SO_SNDBUF);
|
||||
set.add(StandardSocketOptions.SO_RCVBUF);
|
||||
set.add(StandardSocketOptions.SO_KEEPALIVE);
|
||||
set.add(StandardSocketOptions.SO_REUSEADDR);
|
||||
set.add(StandardSocketOptions.TCP_NODELAY);
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
|
||||
private final Socket socket;
|
||||
|
||||
private final ReadableByteChannel readChannel;
|
||||
|
||||
private final WritableByteChannel writeChannel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
public TcpBioAsyncConnection(final Socket socket, final SocketAddress addr0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
this.socket = socket;
|
||||
ReadableByteChannel rc = null;
|
||||
WritableByteChannel wc = null;
|
||||
try {
|
||||
socket.setSoTimeout(Math.max(readTimeoutSeconds0, writeTimeoutSeconds0));
|
||||
rc = Channels.newChannel(socket.getInputStream());
|
||||
wc = Channels.newChannel(socket.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.readChannel = rc;
|
||||
this.writeChannel = wc;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds0;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = socket.getRemoteSocketAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
this.livingCounter = livingCounter;
|
||||
this.closedCounter = closedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
return socket.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSeconds() {
|
||||
return readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownInput() {
|
||||
try {
|
||||
this.socket.shutdownInput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownOutput() {
|
||||
try {
|
||||
this.socket.shutdownOutput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(SocketOption<T> name, T value) {
|
||||
try {
|
||||
if (StandardSocketOptions.SO_REUSEADDR == name) {
|
||||
this.socket.setReuseAddress((Boolean) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.SO_KEEPALIVE == name) {
|
||||
this.socket.setKeepAlive((Boolean) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.TCP_NODELAY == name) {
|
||||
this.socket.setTcpNoDelay((Boolean) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.SO_RCVBUF == name) {
|
||||
this.socket.setReceiveBufferSize((Integer) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.SO_SNDBUF == name) {
|
||||
this.socket.setSendBufferSize((Integer) value);
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return defaultOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
rs += writeChannel.write(srcs[i]);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
read(dst, attachment, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return !socket.isClosed();
|
||||
}
|
||||
}
|
||||
365
src/org/redkale/net/TcpNioAsyncConnection.java
Normal file
365
src/org/redkale/net/TcpNioAsyncConnection.java
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpNioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
|
||||
private final Selector selector;
|
||||
|
||||
private SelectionKey key;
|
||||
|
||||
private final SocketChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
ByteBuffer readBuffer;
|
||||
|
||||
Object readAttachment;
|
||||
|
||||
CompletionHandler readHandler;
|
||||
|
||||
ByteBuffer writeOneBuffer;
|
||||
|
||||
ByteBuffer[] writeBuffers;
|
||||
|
||||
int writingCount;
|
||||
|
||||
int writeOffset;
|
||||
|
||||
int writeLength;
|
||||
|
||||
Object writeAttachment;
|
||||
|
||||
CompletionHandler writeHandler;
|
||||
|
||||
public TcpNioAsyncConnection(final SocketChannel ch, SocketAddress addr0,
|
||||
final Selector selector,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
this.channel = ch;
|
||||
this.selector = selector;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds0;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = ch.getRemoteAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
this.livingCounter = livingCounter;
|
||||
this.closedCounter = closedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSeconds() {
|
||||
return this.readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return this.writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
try {
|
||||
return channel.getLocalAddress();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownInput() {
|
||||
try {
|
||||
this.channel.shutdownInput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownOutput() {
|
||||
try {
|
||||
this.channel.shutdownOutput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(SocketOption<T> name, T value) {
|
||||
try {
|
||||
this.channel.setOption(name, value);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return this.channel.supportedOptions();
|
||||
}
|
||||
|
||||
CompletionHandler removeReadHandler() {
|
||||
CompletionHandler handler = this.readHandler;
|
||||
this.readHandler = null;
|
||||
return handler;
|
||||
}
|
||||
|
||||
ByteBuffer removeReadBuffer() {
|
||||
ByteBuffer buffer = this.readBuffer;
|
||||
this.readBuffer = null;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Object removeReadAttachment() {
|
||||
Object attach = this.readAttachment;
|
||||
this.readAttachment = null;
|
||||
return attach;
|
||||
}
|
||||
|
||||
void completeRead(int rs) {
|
||||
Object attach = this.readAttachment;
|
||||
CompletionHandler handler = this.readHandler;
|
||||
this.readBuffer = null;
|
||||
this.readAttachment = null;
|
||||
this.readHandler = null;
|
||||
handler.completed(rs, attach);
|
||||
}
|
||||
|
||||
void faileRead(Throwable t) {
|
||||
Object attach = this.readAttachment;
|
||||
CompletionHandler handler = this.readHandler;
|
||||
this.readBuffer = null;
|
||||
this.readAttachment = null;
|
||||
this.readHandler = null;
|
||||
handler.failed(t, attach);
|
||||
}
|
||||
|
||||
CompletionHandler removeWriteHandler() {
|
||||
CompletionHandler handler = this.writeHandler;
|
||||
this.writeHandler = null;
|
||||
return handler;
|
||||
}
|
||||
|
||||
ByteBuffer removeWriteOneBuffer() {
|
||||
ByteBuffer buffer = this.writeOneBuffer;
|
||||
this.writeOneBuffer = null;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ByteBuffer[] removeWriteBuffers() {
|
||||
ByteBuffer[] buffers = this.writeBuffers;
|
||||
this.writeBuffers = null;
|
||||
return buffers;
|
||||
}
|
||||
|
||||
int removeWritingCount() {
|
||||
int rs = this.writingCount;
|
||||
this.writingCount = 0;
|
||||
return rs;
|
||||
}
|
||||
|
||||
int removeWriteOffset() {
|
||||
int rs = this.writeOffset;
|
||||
this.writeOffset = 0;
|
||||
return rs;
|
||||
}
|
||||
|
||||
int removeWriteLength() {
|
||||
int rs = this.writeLength;
|
||||
this.writeLength = 0;
|
||||
return rs;
|
||||
}
|
||||
|
||||
Object removeWriteAttachment() {
|
||||
Object attach = this.writeAttachment;
|
||||
this.writeAttachment = null;
|
||||
return attach;
|
||||
}
|
||||
|
||||
void completeWrite(int rs) {
|
||||
Object attach = this.writeAttachment;
|
||||
CompletionHandler handler = this.writeHandler;
|
||||
this.writeOneBuffer = null;
|
||||
this.writeBuffers = null;
|
||||
this.writeOffset = 0;
|
||||
this.writeLength = 0;
|
||||
this.writeAttachment = null;
|
||||
this.writeHandler = null;
|
||||
handler.completed(rs, attach);
|
||||
}
|
||||
|
||||
void faileWrite(Throwable t) {
|
||||
Object attach = this.writeAttachment;
|
||||
CompletionHandler handler = this.writeHandler;
|
||||
this.writeOneBuffer = null;
|
||||
this.writeBuffers = null;
|
||||
this.writeOffset = 0;
|
||||
this.writeLength = 0;
|
||||
this.writeAttachment = null;
|
||||
this.writeHandler = null;
|
||||
handler.failed(t, attach);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (this.readHandler != null) throw new RuntimeException("pending read");
|
||||
try {
|
||||
this.readBuffer = dst;
|
||||
this.readAttachment = attachment;
|
||||
this.readHandler = handler;
|
||||
if (key == null) {
|
||||
key = channel.register(selector, SelectionKey.OP_READ);
|
||||
key.attach(this);
|
||||
} else {
|
||||
key.interestOps(SelectionKey.OP_READ);
|
||||
}
|
||||
selector.wakeup();
|
||||
} catch (Exception e) {
|
||||
faileRead(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
read(dst, attachment, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
read(dst, null, new CompletionHandler<Integer, Void>() {
|
||||
@Override
|
||||
public void completed(Integer result, Void attachment) {
|
||||
future.complete(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
future.completeExceptionally(exc);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (this.writeHandler != null) throw new RuntimeException("pending write");
|
||||
try {
|
||||
this.writeBuffers = srcs;
|
||||
this.writeOffset = offset;
|
||||
this.writeLength = length;
|
||||
this.writingCount = 0;
|
||||
this.writeAttachment = attachment;
|
||||
this.writeHandler = handler;
|
||||
if (key == null) {
|
||||
key = channel.register(selector, SelectionKey.OP_WRITE);
|
||||
key.attach(this);
|
||||
} else {
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
selector.wakeup();
|
||||
} catch (Exception e) {
|
||||
faileWrite(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (this.writeHandler != null) throw new RuntimeException("pending write");
|
||||
try {
|
||||
this.writeOneBuffer = src;
|
||||
this.writingCount = 0;
|
||||
this.writeAttachment = attachment;
|
||||
this.writeHandler = handler;
|
||||
if (key == null) {
|
||||
key = channel.register(selector, SelectionKey.OP_WRITE);
|
||||
key.attach(this);
|
||||
} else {
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
selector.wakeup();
|
||||
} catch (Exception e) {
|
||||
faileWrite(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
write(src, null, new CompletionHandler<Integer, Void>() {
|
||||
@Override
|
||||
public void completed(Integer result, Void attachment) {
|
||||
future.complete(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
future.completeExceptionally(exc);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
channel.close();
|
||||
key.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
309
src/org/redkale/net/TcpNioProtocolServer.java
Normal file
309
src/org/redkale/net/TcpNioProtocolServer.java
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 协议底层Server
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpNioProtocolServer extends ProtocolServer {
|
||||
|
||||
private Selector acceptSelector;
|
||||
|
||||
private ServerSocketChannel serverChannel;
|
||||
|
||||
private NIOThreadWorker[] workers;
|
||||
|
||||
private NIOThreadWorker currWorker;
|
||||
|
||||
private boolean running;
|
||||
|
||||
public TcpNioProtocolServer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(AnyValue config) throws IOException {
|
||||
acceptSelector = Selector.open();
|
||||
this.serverChannel = ServerSocketChannel.open();
|
||||
serverChannel.configureBlocking(false);
|
||||
ServerSocket socket = serverChannel.socket();
|
||||
socket.setReceiveBufferSize(16 * 1024);
|
||||
socket.setReuseAddress(true);
|
||||
|
||||
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
|
||||
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
this.serverChannel.bind(local, backlog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<SocketOption<?>> supportedOptions() {
|
||||
return this.serverChannel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
this.serverChannel.setOption(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() throws IOException {
|
||||
this.serverChannel.register(acceptSelector, SelectionKey.OP_ACCEPT);
|
||||
final CountDownLatch cdl = new CountDownLatch(1);
|
||||
this.running = true;
|
||||
this.workers = new NIOThreadWorker[Runtime.getRuntime().availableProcessors()];
|
||||
for (int i = 0; i < workers.length; i++) {
|
||||
workers[i] = new NIOThreadWorker();
|
||||
workers[i].setDaemon(true);
|
||||
workers[i].start();
|
||||
}
|
||||
for (int i = 0; i < workers.length - 1; i++) { //构成环形
|
||||
workers[i].next = workers[i + 1];
|
||||
}
|
||||
workers[workers.length - 1].next = workers[0];
|
||||
currWorker = workers[0];
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
cdl.countDown();
|
||||
while (running) {
|
||||
try {
|
||||
acceptSelector.select();
|
||||
Set<SelectionKey> selectedKeys = acceptSelector.selectedKeys();
|
||||
synchronized (selectedKeys) {
|
||||
Iterator<?> iter = selectedKeys.iterator();
|
||||
while (iter.hasNext()) {
|
||||
SelectionKey key = (SelectionKey) iter.next();
|
||||
iter.remove();
|
||||
if (key.isAcceptable()) {
|
||||
try {
|
||||
SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
|
||||
channel.configureBlocking(false);
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
createCounter.incrementAndGet();
|
||||
livingCounter.incrementAndGet();
|
||||
currWorker.addChannel(channel);
|
||||
currWorker = currWorker.next;
|
||||
} catch (IOException io) {
|
||||
io.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
cdl.await();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!this.running) return;
|
||||
this.running = false;
|
||||
serverChannel.close();
|
||||
acceptSelector.close();
|
||||
for (NIOThreadWorker worker : workers) {
|
||||
worker.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
class NIOThreadWorker extends Thread {
|
||||
|
||||
final Selector selector;
|
||||
|
||||
NIOThreadWorker next;
|
||||
|
||||
public NIOThreadWorker() {
|
||||
try {
|
||||
this.selector = Selector.open();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addChannel(SocketChannel channel) throws IOException {
|
||||
AsyncConnection conn = new TcpNioAsyncConnection(channel, null, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
context.runAsync(new PrepareRunner(context, conn, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
try {
|
||||
selector.select(50);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
Set<SelectionKey> selectedKeys = selector.selectedKeys();
|
||||
synchronized (selectedKeys) {
|
||||
Iterator<?> iter = selectedKeys.iterator();
|
||||
while (iter.hasNext()) {
|
||||
SelectionKey key = (SelectionKey) iter.next();
|
||||
iter.remove();
|
||||
processKey(key);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processKey(SelectionKey key) {
|
||||
if (key == null || !key.isValid()) return;
|
||||
SocketChannel socket = (SocketChannel) key.channel();
|
||||
TcpNioAsyncConnection conn = (TcpNioAsyncConnection) key.attachment();
|
||||
if (!socket.isOpen()) {
|
||||
if (conn == null) {
|
||||
key.cancel();
|
||||
} else {
|
||||
conn.dispose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (conn == null) return;
|
||||
if (key.isWritable()) {
|
||||
if (conn.writeHandler != null) writeOP(key, socket, conn);
|
||||
} else if (key.isReadable()) {
|
||||
if (conn.readHandler != null) readOP(key, socket, conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void readOP(SelectionKey key, SocketChannel socket, TcpNioAsyncConnection conn) {
|
||||
final CompletionHandler handler = conn.removeReadHandler();
|
||||
final ByteBuffer buffer = conn.removeReadBuffer();
|
||||
final Object attach = conn.removeReadAttachment();
|
||||
//System.out.println(conn + "------readbuf:" + buffer + "-------handler:" + handler);
|
||||
if (handler == null || buffer == null) return;
|
||||
try {
|
||||
final int rs = socket.read(buffer);
|
||||
{ //测试
|
||||
buffer.flip();
|
||||
byte[] bs = new byte[buffer.remaining()];
|
||||
buffer.get(bs);
|
||||
//System.out.println(conn + "------readbuf:" + buffer + "-------handler:" + handler + "-------读内容: " + new String(bs));
|
||||
}
|
||||
//System.out.println(conn + "------readbuf:" + buffer + "-------handler:" + handler + "-------read: " + rs);
|
||||
context.runAsync(() -> {
|
||||
try {
|
||||
handler.completed(rs, attach);
|
||||
} catch (Throwable e) {
|
||||
handler.failed(e, attach);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
context.runAsync(() -> handler.failed(t, attach));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeOP(SelectionKey key, SocketChannel socket, TcpNioAsyncConnection conn) {
|
||||
final CompletionHandler handler = conn.writeHandler;
|
||||
final ByteBuffer oneBuffer = conn.removeWriteOneBuffer();
|
||||
final ByteBuffer[] buffers = conn.removeWriteBuffers();
|
||||
final Object attach = conn.removeWriteAttachment();
|
||||
final int writingCount = conn.removeWritingCount();
|
||||
final int writeOffset = conn.removeWriteOffset();
|
||||
final int writeLength = conn.removeWriteLength();
|
||||
if (handler == null || (oneBuffer == null && buffers == null)) return;
|
||||
//System.out.println(conn + "------buffers:" + Arrays.toString(buffers) + "---onebuf:" + oneBuffer + "-------handler:" + handler);
|
||||
try {
|
||||
int rs = 0;
|
||||
if (oneBuffer == null) {
|
||||
int offset = writeOffset;
|
||||
int length = writeLength;
|
||||
rs = (int) socket.write(buffers, offset, length);
|
||||
boolean over = true;
|
||||
int end = offset + length;
|
||||
for (int i = offset; i < end; i++) {
|
||||
if (buffers[i].hasRemaining()) {
|
||||
over = false;
|
||||
length -= i - offset;
|
||||
offset = i;
|
||||
}
|
||||
}
|
||||
if (!over) {
|
||||
conn.writingCount += rs;
|
||||
conn.writeHandler = handler;
|
||||
conn.writeAttachment = attach;
|
||||
conn.writeBuffers = buffers;
|
||||
conn.writeOffset = offset;
|
||||
conn.writeLength = length;
|
||||
key.interestOps(SelectionKey.OP_READ + SelectionKey.OP_WRITE);
|
||||
key.selector().wakeup();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
rs = socket.write(oneBuffer);
|
||||
if (oneBuffer.hasRemaining()) {
|
||||
conn.writingCount += rs;
|
||||
conn.writeHandler = handler;
|
||||
conn.writeAttachment = attach;
|
||||
conn.writeOneBuffer = oneBuffer;
|
||||
key.interestOps(SelectionKey.OP_READ + SelectionKey.OP_WRITE);
|
||||
key.selector().wakeup();
|
||||
return;
|
||||
}
|
||||
}
|
||||
conn.removeWriteHandler();
|
||||
key.interestOps(SelectionKey.OP_READ); //OP_CONNECT
|
||||
final int rs0 = rs + writingCount;
|
||||
//System.out.println(conn + "------buffers:" + Arrays.toString(buffers) + "---onebuf:" + oneBuffer + "-------handler:" + handler + "-------write: " + rs);
|
||||
context.runAsync(() -> {
|
||||
try {
|
||||
handler.completed(rs0, attach);
|
||||
} catch (Throwable e) {
|
||||
handler.failed(e, attach);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
context.runAsync(() -> handler.failed(t, attach));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,14 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import javax.net.ssl.SSLContext;
|
||||
@@ -30,20 +32,7 @@ public final class Transport {
|
||||
|
||||
public static final String DEFAULT_PROTOCOL = "TCP";
|
||||
|
||||
protected static final int MAX_POOL_LIMIT = Runtime.getRuntime().availableProcessors() * 16;
|
||||
|
||||
protected static final boolean supportTcpNoDelay;
|
||||
|
||||
static {
|
||||
boolean tcpNoDelay = false;
|
||||
try {
|
||||
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
|
||||
tcpNoDelay = channel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY);
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
supportTcpNoDelay = tcpNoDelay;
|
||||
}
|
||||
protected final AtomicInteger seq = new AtomicInteger(-1);
|
||||
|
||||
protected final TransportFactory factory;
|
||||
|
||||
@@ -59,7 +48,8 @@ public final class Transport {
|
||||
|
||||
protected final InetSocketAddress clientAddress;
|
||||
|
||||
protected TransportAddress[] transportAddres = new TransportAddress[0];
|
||||
//不可能为null
|
||||
protected TransportNode[] transportNodes = new TransportNode[0];
|
||||
|
||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
||||
|
||||
@@ -68,8 +58,6 @@ public final class Transport {
|
||||
//负载均衡策略
|
||||
protected final TransportStrategy strategy;
|
||||
|
||||
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
|
||||
|
||||
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
|
||||
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
|
||||
@@ -95,33 +83,44 @@ public final class Transport {
|
||||
}
|
||||
|
||||
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
|
||||
TransportAddress[] oldAddresses = this.transportAddres;
|
||||
List<TransportAddress> list = new ArrayList<>();
|
||||
final TransportNode[] oldNodes = this.transportNodes;
|
||||
synchronized (this) {
|
||||
List<TransportNode> list = new ArrayList<>();
|
||||
if (addresses != null) {
|
||||
for (InetSocketAddress addr : addresses) {
|
||||
if (clientAddress != null && clientAddress.equals(addr)) continue;
|
||||
list.add(new TransportAddress(addr));
|
||||
boolean hasold = false;
|
||||
for (TransportNode oldAddr : oldNodes) {
|
||||
if (oldAddr.getAddress().equals(addr)) {
|
||||
list.add(oldAddr);
|
||||
hasold = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.transportAddres = list.toArray(new TransportAddress[list.size()]);
|
||||
|
||||
InetSocketAddress[] rs = new InetSocketAddress[oldAddresses.length];
|
||||
if (hasold) continue;
|
||||
list.add(new TransportNode(factory.poolmaxconns, addr));
|
||||
}
|
||||
}
|
||||
this.transportNodes = list.toArray(new TransportNode[list.size()]);
|
||||
}
|
||||
InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length];
|
||||
for (int i = 0; i < rs.length; i++) {
|
||||
rs[i] = oldAddresses[i].getAddress();
|
||||
rs[i] = oldNodes[i].getAddress();
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
public final boolean addRemoteAddresses(final InetSocketAddress addr) {
|
||||
if (addr == null) return false;
|
||||
if (clientAddress != null && clientAddress.equals(addr)) return false;
|
||||
synchronized (this) {
|
||||
if (this.transportAddres == null) {
|
||||
this.transportAddres = new TransportAddress[]{new TransportAddress(addr)};
|
||||
if (this.transportNodes.length == 0) {
|
||||
this.transportNodes = new TransportNode[]{new TransportNode(factory.poolmaxconns, addr)};
|
||||
} else {
|
||||
for (TransportAddress i : this.transportAddres) {
|
||||
for (TransportNode i : this.transportNodes) {
|
||||
if (addr.equals(i.address)) return false;
|
||||
}
|
||||
this.transportAddres = Utility.append(transportAddres, new TransportAddress(addr));
|
||||
this.transportNodes = Utility.append(transportNodes, new TransportNode(factory.poolmaxconns, addr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -129,9 +128,8 @@ public final class Transport {
|
||||
|
||||
public final boolean removeRemoteAddresses(InetSocketAddress addr) {
|
||||
if (addr == null) return false;
|
||||
if (this.transportAddres == null) return false;
|
||||
synchronized (this) {
|
||||
this.transportAddres = Utility.remove(transportAddres, new TransportAddress(addr));
|
||||
this.transportNodes = Utility.remove(transportNodes, new TransportNode(factory.poolmaxconns, addr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -145,32 +143,39 @@ public final class Transport {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
connPool.forEach((k, v) -> v.forEach(c -> c.dispose()));
|
||||
TransportNode[] nodes = this.transportNodes;
|
||||
if (nodes == null) return;
|
||||
for (TransportNode node : nodes) {
|
||||
if (node != null) node.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public InetSocketAddress getClientAddress() {
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
public TransportAddress[] getTransportAddresses() {
|
||||
return transportAddres;
|
||||
public TransportNode[] getTransportNodes() {
|
||||
return transportNodes;
|
||||
}
|
||||
|
||||
public TransportNode findTransportNode(SocketAddress addr) {
|
||||
for (TransportNode node : this.transportNodes) {
|
||||
if (node.address.equals(addr)) return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public InetSocketAddress[] getRemoteAddresses() {
|
||||
InetSocketAddress[] rs = new InetSocketAddress[transportAddres.length];
|
||||
InetSocketAddress[] rs = new InetSocketAddress[transportNodes.length];
|
||||
for (int i = 0; i < rs.length; i++) {
|
||||
rs[i] = transportAddres[i].getAddress();
|
||||
rs[i] = transportNodes[i].getAddress();
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> getAsyncConnectionPool() {
|
||||
return connPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteAddres = " + Arrays.toString(transportAddres) + "}";
|
||||
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
|
||||
}
|
||||
|
||||
public ByteBuffer pollBuffer() {
|
||||
@@ -189,90 +194,166 @@ public final class Transport {
|
||||
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
|
||||
}
|
||||
|
||||
public AsynchronousChannelGroup getTransportChannelGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public boolean isTCP() {
|
||||
return tcp;
|
||||
}
|
||||
|
||||
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr) {
|
||||
if (this.strategy != null) return strategy.pollConnection(addr, this);
|
||||
if (addr == null && this.transportAddres.length == 1) addr = this.transportAddres[0].address;
|
||||
final boolean rand = addr == null;
|
||||
if (rand && this.transportAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
|
||||
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
|
||||
if (this.strategy != null) return strategy.pollConnection(addr0, this);
|
||||
final TransportNode[] nodes = this.transportNodes;
|
||||
if (addr0 == null && nodes.length == 1) addr0 = nodes[0].address;
|
||||
final SocketAddress addr = addr0;
|
||||
final boolean rand = addr == null; //是否随机取地址
|
||||
if (rand && nodes.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
|
||||
try {
|
||||
if (tcp) {
|
||||
AsynchronousSocketChannel channel = null;
|
||||
if (rand) { //取地址
|
||||
TransportAddress transportAddr;
|
||||
boolean tryed = false;
|
||||
for (int i = 0; i < transportAddres.length; i++) {
|
||||
transportAddr = transportAddres[i];
|
||||
addr = transportAddr.address;
|
||||
if (!transportAddr.enable) continue;
|
||||
final BlockingQueue<AsyncConnection> queue = transportAddr.conns;
|
||||
if (!tcp) { // UDP
|
||||
SocketAddress udpaddr = rand ? nodes[0].address : addr;
|
||||
DatagramChannel channel = DatagramChannel.open();
|
||||
channel.configureBlocking(true);
|
||||
channel.connect(udpaddr);
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(channel, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
|
||||
}
|
||||
if (!rand) { //指定地址
|
||||
TransportNode node = findTransportNode(addr);
|
||||
if (node == null) {
|
||||
return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
}
|
||||
final BlockingQueue<AsyncConnection> queue = node.conns;
|
||||
if (!queue.isEmpty()) {
|
||||
AsyncConnection conn;
|
||||
while ((conn = queue.poll()) != null) {
|
||||
if (conn.isOpen()) return CompletableFuture.completedFuture(conn);
|
||||
}
|
||||
}
|
||||
tryed = true;
|
||||
if (channel == null) {
|
||||
channel = AsynchronousSocketChannel.open(group);
|
||||
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
try {
|
||||
channel.connect(addr).get(2, TimeUnit.SECONDS);
|
||||
transportAddr.enable = true;
|
||||
break;
|
||||
} catch (Exception iex) {
|
||||
transportAddr.enable = false;
|
||||
channel = null;
|
||||
}
|
||||
}
|
||||
if (channel == null && !tryed) {
|
||||
for (int i = 0; i < transportAddres.length; i++) {
|
||||
transportAddr = transportAddres[i];
|
||||
addr = transportAddr.address;
|
||||
if (channel == null) {
|
||||
channel = AsynchronousSocketChannel.open(group);
|
||||
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
try {
|
||||
channel.connect(addr).get(2, TimeUnit.SECONDS);
|
||||
transportAddr.enable = true;
|
||||
break;
|
||||
} catch (Exception iex) {
|
||||
transportAddr.enable = false;
|
||||
channel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (conn.isOpen()) {
|
||||
return CompletableFuture.completedFuture(conn);
|
||||
} else {
|
||||
return AsyncConnection.createTCP(group, sslContext, addr, supportTcpNoDelay, 6, 6);
|
||||
conn.dispose();
|
||||
}
|
||||
if (channel == null) return CompletableFuture.completedFuture(null);
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, 6, 6));
|
||||
} else { // UDP
|
||||
if (rand) addr = this.transportAddres[0].address;
|
||||
DatagramChannel channel = DatagramChannel.open();
|
||||
channel.configureBlocking(true);
|
||||
channel.connect(addr);
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, true, 6, 6));
|
||||
}
|
||||
}
|
||||
return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
//---------------------随机取地址------------------------
|
||||
int enablecount = 0;
|
||||
final TransportNode[] newnodes = new TransportNode[nodes.length];
|
||||
for (final TransportNode node : nodes) {
|
||||
if (node.disabletime > 0) continue;
|
||||
newnodes[enablecount++] = node;
|
||||
}
|
||||
final long now = System.currentTimeMillis();
|
||||
if (enablecount > 0) { //存在可用的地址
|
||||
final TransportNode one = newnodes[Math.abs(seq.incrementAndGet()) % enablecount];
|
||||
final BlockingQueue<AsyncConnection> queue = one.conns;
|
||||
if (!queue.isEmpty()) {
|
||||
AsyncConnection conn;
|
||||
while ((conn = queue.poll()) != null) {
|
||||
if (conn.isOpen()) {
|
||||
return CompletableFuture.completedFuture(conn);
|
||||
} else {
|
||||
conn.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
|
||||
@Override
|
||||
public void completed(Void result, TransportNode attachment) {
|
||||
attachment.disabletime = 0;
|
||||
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
if (future.isDone()) {
|
||||
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
||||
} else {
|
||||
future.complete(asyncConn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, TransportNode attachment) {
|
||||
attachment.disabletime = now;
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
pollConnection0(nodes, one, now).whenComplete((r, t) -> {
|
||||
if (t != null) {
|
||||
future.completeExceptionally(t);
|
||||
} else {
|
||||
future.complete(r);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
return pollConnection0(nodes, null, now);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("transport address = " + addr, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<AsyncConnection> pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException {
|
||||
//从可用/不可用的地址列表中创建连接
|
||||
AtomicInteger count = new AtomicInteger(nodes.length);
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
for (final TransportNode node : nodes) {
|
||||
if (node == exclude) continue;
|
||||
if (future.isDone()) return future;
|
||||
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.connect(node.address, node, new CompletionHandler<Void, TransportNode>() {
|
||||
@Override
|
||||
public void completed(Void result, TransportNode attachment) {
|
||||
try {
|
||||
attachment.disabletime = 0;
|
||||
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
if (future.isDone()) {
|
||||
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
||||
} else {
|
||||
future.complete(asyncConn);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, TransportNode attachment) {
|
||||
attachment.disabletime = now;
|
||||
if (count.decrementAndGet() < 1) {
|
||||
future.completeExceptionally(exc);
|
||||
}
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public void offerConnection(final boolean forceClose, AsyncConnection conn) {
|
||||
if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return;
|
||||
if (!forceClose && conn.isTCP()) {
|
||||
if (conn.isOpen()) {
|
||||
BlockingQueue<AsyncConnection> queue = connPool.get(conn.getRemoteAddress());
|
||||
if (queue == null) {
|
||||
queue = new ArrayBlockingQueue<>(MAX_POOL_LIMIT);
|
||||
connPool.put(conn.getRemoteAddress(), queue);
|
||||
}
|
||||
if (!queue.offer(conn)) conn.dispose();
|
||||
TransportNode node = findTransportNode(conn.getRemoteAddress());
|
||||
if (node == null || !node.conns.offer(conn)) conn.dispose();
|
||||
} else {
|
||||
conn.dispose();
|
||||
}
|
||||
} else {
|
||||
conn.dispose();
|
||||
@@ -317,38 +398,82 @@ public final class Transport {
|
||||
});
|
||||
}
|
||||
|
||||
public static class TransportAddress {
|
||||
public static class TransportNode {
|
||||
|
||||
protected InetSocketAddress address;
|
||||
|
||||
protected volatile boolean enable;
|
||||
protected volatile long disabletime; //不可用时的时间, 为0表示可用
|
||||
|
||||
protected final BlockingQueue<AsyncConnection> conns = new ArrayBlockingQueue<>(MAX_POOL_LIMIT);
|
||||
protected final BlockingQueue<AsyncConnection> conns;
|
||||
|
||||
public TransportAddress(InetSocketAddress address) {
|
||||
protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<>();
|
||||
|
||||
public TransportNode(int poolmaxconns, InetSocketAddress address) {
|
||||
this.address = address;
|
||||
this.enable = true;
|
||||
this.disabletime = 0;
|
||||
this.conns = new ArrayBlockingQueue<>(poolmaxconns);
|
||||
}
|
||||
|
||||
@ConstructorParameters({"address", "enable"})
|
||||
public TransportAddress(InetSocketAddress address, boolean enable) {
|
||||
@ConstructorParameters({"poolmaxconns", "address", "disabletime"})
|
||||
public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) {
|
||||
this.address = address;
|
||||
this.enable = enable;
|
||||
this.disabletime = disabletime;
|
||||
this.conns = new LinkedBlockingQueue<>(poolmaxconns);
|
||||
}
|
||||
|
||||
public int getPoolmaxconns() {
|
||||
return this.conns.remainingCapacity() + this.conns.size();
|
||||
}
|
||||
|
||||
public <T> T setAttribute(String name, T value) {
|
||||
attributes.put(name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getAttribute(String name) {
|
||||
return (T) attributes.get(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T removeAttribute(String name) {
|
||||
return (T) attributes.remove(name);
|
||||
}
|
||||
|
||||
public TransportNode clearAttributes() {
|
||||
attributes.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(ConcurrentHashMap<String, Object> map) {
|
||||
attributes.clear();
|
||||
if (map != null) attributes.putAll(map);
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
public long getDisabletime() {
|
||||
return disabletime;
|
||||
}
|
||||
|
||||
@ConvertColumn(ignore = true)
|
||||
@ConvertDisabled
|
||||
public BlockingQueue<AsyncConnection> getConns() {
|
||||
return conns;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
AsyncConnection conn;
|
||||
while ((conn = conns.poll()) != null) {
|
||||
conn.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.address.hashCode();
|
||||
@@ -359,10 +484,11 @@ public final class Transport {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
final TransportAddress other = (TransportAddress) obj;
|
||||
final TransportNode other = (TransportNode) obj;
|
||||
return this.address.equals(other.address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
@@ -22,6 +22,7 @@ import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* System.getProperty("net.transport.pinginterval", "30") 心跳周期,默认30秒
|
||||
* System.getProperty("net.transport.checkinterval", "30") 检查不可用地址周期,默认30秒
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -31,13 +32,17 @@ import org.redkale.util.*;
|
||||
public class TransportFactory {
|
||||
|
||||
@Comment("默认TCP读取超时秒数")
|
||||
public static int DEFAULT_READTIMEOUTSECOND = 6;
|
||||
public static int DEFAULT_READTIMEOUTSECONDS = 6;
|
||||
|
||||
@Comment("默认TCP写入超时秒数")
|
||||
public static int DEFAULT_WRITETIMEOUTSECOND = 6;
|
||||
public static int DEFAULT_WRITETIMEOUTSECONDS = 6;
|
||||
|
||||
public static final String NAME_POOLMAXCONNS = "poolmaxconns";
|
||||
|
||||
public static final String NAME_PINGINTERVAL = "pinginterval";
|
||||
|
||||
public static final String NAME_CHECKINTERVAL = "checkinterval";
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
|
||||
|
||||
//传输端的线程池
|
||||
@@ -59,17 +64,23 @@ public class TransportFactory {
|
||||
|
||||
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
|
||||
|
||||
//连接池大小
|
||||
protected int poolmaxconns = Integer.getInteger("net.transport.poolmaxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍
|
||||
|
||||
//检查不可用地址周期, 单位:秒
|
||||
protected int checkinterval = Integer.getInteger("net.transport.checkinterval", 30);
|
||||
|
||||
//心跳周期, 单位:秒
|
||||
protected int pinginterval;
|
||||
|
||||
//TCP读取超时秒数
|
||||
protected int readTimeoutSecond;
|
||||
protected int readTimeoutSeconds;
|
||||
|
||||
//TCP写入超时秒数
|
||||
protected int writeTimeoutSecond;
|
||||
protected int writeTimeoutSeconds;
|
||||
|
||||
//ping的定时器
|
||||
private ScheduledThreadPoolExecutor pingScheduler;
|
||||
//ping和检查的定时器
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
protected SSLContext sslContext;
|
||||
|
||||
@@ -83,35 +94,49 @@ public class TransportFactory {
|
||||
protected final TransportStrategy strategy;
|
||||
|
||||
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
SSLContext sslContext, int readTimeoutSecond, int writeTimeoutSecond, final TransportStrategy strategy) {
|
||||
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
|
||||
this.executor = executor;
|
||||
this.bufferPool = bufferPool;
|
||||
this.channelGroup = channelGroup;
|
||||
this.sslContext = sslContext;
|
||||
this.readTimeoutSecond = readTimeoutSecond;
|
||||
this.writeTimeoutSecond = writeTimeoutSecond;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
SSLContext sslContext, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
this(executor, bufferPool, channelGroup, sslContext, readTimeoutSecond, writeTimeoutSecond, null);
|
||||
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
this(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
|
||||
}
|
||||
|
||||
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
|
||||
if (conf != null) {
|
||||
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, 0);
|
||||
this.poolmaxconns = conf.getIntValue(NAME_POOLMAXCONNS, this.poolmaxconns);
|
||||
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, this.pinginterval);
|
||||
this.checkinterval = conf.getIntValue(NAME_CHECKINTERVAL, this.checkinterval);
|
||||
if (this.poolmaxconns < 2) this.poolmaxconns = 2;
|
||||
if (this.pinginterval < 2) this.pinginterval = 2;
|
||||
if (this.checkinterval < 2) this.checkinterval = 2;
|
||||
}
|
||||
if (this.pinginterval > 0) {
|
||||
if (this.pingScheduler == null && pingBuffer != null) {
|
||||
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
|
||||
this.pongLength = pongLength;
|
||||
this.pingScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryPingTask-Thread");
|
||||
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryTask-Thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
pingScheduler.scheduleAtFixedRate(() -> {
|
||||
this.scheduler.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
checks();
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "TransportFactory schedule(interval=" + checkinterval + "s) check error", t);
|
||||
}
|
||||
}, checkinterval, checkinterval, TimeUnit.SECONDS);
|
||||
|
||||
if (this.pinginterval > 0) {
|
||||
if (pingBuffer != null) {
|
||||
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
|
||||
this.pongLength = pongLength;
|
||||
|
||||
scheduler.scheduleAtFixedRate(() -> {
|
||||
pings();
|
||||
}, pinginterval, pinginterval, TimeUnit.SECONDS);
|
||||
}
|
||||
@@ -119,14 +144,14 @@ public class TransportFactory {
|
||||
}
|
||||
|
||||
public static TransportFactory create(int threads) {
|
||||
return create(threads, threads * 2, 8 * 1024, DEFAULT_READTIMEOUTSECOND, DEFAULT_WRITETIMEOUTSECOND);
|
||||
return create(threads, threads * 2, 8 * 1024, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
|
||||
}
|
||||
|
||||
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity) {
|
||||
return create(threads, bufferPoolSize, bufferCapacity, DEFAULT_READTIMEOUTSECOND, DEFAULT_WRITETIMEOUTSECOND);
|
||||
return create(threads, bufferPoolSize, bufferCapacity, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
|
||||
}
|
||||
|
||||
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity, int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
final ObjectPool<ByteBuffer> transportPool = new ObjectPool<>(new AtomicLong(), new AtomicLong(), bufferPoolSize,
|
||||
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
|
||||
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
|
||||
@@ -146,35 +171,35 @@ public class TransportFactory {
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return create(transportExec, transportPool, transportGroup, readTimeoutSecond, writeTimeoutSecond);
|
||||
return create(transportExec, transportPool, transportGroup, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null, DEFAULT_READTIMEOUTSECOND, DEFAULT_WRITETIMEOUTSECOND, null);
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSecond, writeTimeoutSecond, null);
|
||||
int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, null);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
int readTimeoutSecond, int writeTimeoutSecond, final TransportStrategy strategy) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSecond, writeTimeoutSecond, strategy);
|
||||
int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, SSLContext sslContext) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, DEFAULT_READTIMEOUTSECOND, DEFAULT_WRITETIMEOUTSECOND, null);
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
SSLContext sslContext, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSecond, writeTimeoutSecond, null);
|
||||
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
SSLContext sslContext, int readTimeoutSecond, int writeTimeoutSecond, final TransportStrategy strategy) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSecond, writeTimeoutSecond, strategy);
|
||||
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, strategy);
|
||||
}
|
||||
|
||||
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||
@@ -309,7 +334,7 @@ public class TransportFactory {
|
||||
}
|
||||
|
||||
public void shutdownNow() {
|
||||
if (this.pingScheduler != null) this.pingScheduler.shutdownNow();
|
||||
if (this.scheduler != null) this.scheduler.shutdownNow();
|
||||
try {
|
||||
this.channelGroup.shutdownNow();
|
||||
} catch (Exception e) {
|
||||
@@ -317,8 +342,7 @@ public class TransportFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private void pings() {
|
||||
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
|
||||
private void checks() {
|
||||
List<WeakReference> nulllist = new ArrayList<>();
|
||||
for (WeakReference<Transport> ref : transportReferences) {
|
||||
Transport transport = ref.get();
|
||||
@@ -326,8 +350,39 @@ public class TransportFactory {
|
||||
nulllist.add(ref);
|
||||
continue;
|
||||
}
|
||||
List<BlockingQueue<AsyncConnection>> list = new ArrayList<>(transport.getAsyncConnectionPool().values());
|
||||
for (final BlockingQueue<AsyncConnection> queue : list) {
|
||||
Transport.TransportNode[] nodes = transport.getTransportNodes();
|
||||
for (final Transport.TransportNode node : nodes) {
|
||||
if (node.disabletime < 1) continue; //可用
|
||||
try {
|
||||
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(transport.group);
|
||||
channel.connect(node.address, node, new CompletionHandler<Void, Transport.TransportNode>() {
|
||||
@Override
|
||||
public void completed(Void result, Transport.TransportNode attachment) {
|
||||
attachment.disabletime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Transport.TransportNode attachment) {
|
||||
attachment.disabletime = System.currentTimeMillis();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (WeakReference ref : nulllist) {
|
||||
transportReferences.remove(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private void pings() {
|
||||
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
|
||||
for (WeakReference<Transport> ref : transportReferences) {
|
||||
Transport transport = ref.get();
|
||||
if (transport == null) continue;
|
||||
Transport.TransportNode[] nodes = transport.getTransportNodes();
|
||||
for (final Transport.TransportNode node : nodes) {
|
||||
final BlockingQueue<AsyncConnection> queue = node.conns;
|
||||
AsyncConnection conn;
|
||||
while ((conn = queue.poll()) != null) {
|
||||
if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作
|
||||
@@ -379,9 +434,6 @@ public class TransportFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (WeakReference ref : nulllist) {
|
||||
transportReferences.remove(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkName(String name) { //不能含特殊字符
|
||||
|
||||
@@ -18,5 +18,25 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public interface TransportStrategy {
|
||||
|
||||
/**
|
||||
* 创建AsyncConnection
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param transport Transport
|
||||
*
|
||||
* @return AsyncConnection
|
||||
*/
|
||||
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr, Transport transport);
|
||||
|
||||
/**
|
||||
* 回收AsyncConnection,返回false表示使用Transport默认的回收实现, 返回true表示自定义回收实现
|
||||
*
|
||||
* @param forceClose 是否强制关闭
|
||||
* @param conn AsyncConnection
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
default boolean offerConnection(final boolean forceClose, AsyncConnection conn) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
193
src/org/redkale/net/UdpBioAsyncConnection.java
Normal file
193
src/org/redkale/net/UdpBioAsyncConnection.java
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class UdpBioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
|
||||
private final DatagramChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
private final boolean client;
|
||||
|
||||
public UdpBioAsyncConnection(final DatagramChannel ch, SocketAddress addr0,
|
||||
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
this.channel = ch;
|
||||
this.client = client0;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds0;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = ch.getRemoteAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
this.livingCounter = livingCounter;
|
||||
this.closedCounter = closedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSeconds() {
|
||||
return this.readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return this.writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
try {
|
||||
return channel.getLocalAddress();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownInput() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownOutput() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(SocketOption<T> name, T value) {
|
||||
try {
|
||||
this.channel.setOption(name, value);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return this.channel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
rs += channel.send(srcs[i], remoteAddress);
|
||||
if (i != offset) Thread.sleep(10);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (Exception e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
read(dst, attachment, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
if (client) channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTCP() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
123
src/org/redkale/net/UdpBioProtocolServer.java
Normal file
123
src/org/redkale/net/UdpBioProtocolServer.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 协议底层Server
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class UdpBioProtocolServer extends ProtocolServer {
|
||||
|
||||
private boolean running;
|
||||
|
||||
private DatagramChannel serverChannel;
|
||||
|
||||
public UdpBioProtocolServer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(AnyValue config) throws IOException {
|
||||
DatagramChannel ch = DatagramChannel.open();
|
||||
ch.configureBlocking(true);
|
||||
this.serverChannel = ch;
|
||||
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
|
||||
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
this.serverChannel.bind(local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
this.serverChannel.setOption(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<SocketOption<?>> supportedOptions() {
|
||||
return this.serverChannel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() throws IOException {
|
||||
final DatagramChannel serchannel = this.serverChannel;
|
||||
final int readTimeoutSeconds = this.context.readTimeoutSeconds;
|
||||
final int writeTimeoutSeconds = this.context.writeTimeoutSeconds;
|
||||
final CountDownLatch cdl = new CountDownLatch(1);
|
||||
this.running = true;
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
cdl.countDown();
|
||||
while (running) {
|
||||
final ByteBuffer buffer = context.pollBuffer();
|
||||
try {
|
||||
SocketAddress address = serchannel.receive(buffer);
|
||||
buffer.flip();
|
||||
AsyncConnection conn = new UdpBioAsyncConnection(serchannel, address, false, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
context.runAsync(new PrepareRunner(context, conn, buffer, null));
|
||||
} catch (Exception e) {
|
||||
context.offerBuffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
cdl.await();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.running = false;
|
||||
this.serverChannel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreateCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getClosedCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLivingCount() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,12 @@
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import org.redkale.asm.MethodDebugVisitor;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.nio.charset.*;
|
||||
import java.security.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.util.function.*;
|
||||
import org.redkale.asm.*;
|
||||
import static org.redkale.asm.Opcodes.*;
|
||||
import org.redkale.net.*;
|
||||
@@ -33,13 +30,8 @@ public class HttpContext extends Context {
|
||||
|
||||
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
|
||||
|
||||
public HttpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext,
|
||||
final int bufferCapacity, final ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
|
||||
int maxbody, Charset charset, InetSocketAddress address, ResourceFactory resourceFactory,
|
||||
PrepareServlet prepare, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
super(serverStartTime, logger, executor, sslContext, bufferCapacity, bufferPool, responsePool,
|
||||
maxbody, charset, address, resourceFactory, prepare, readTimeoutSecond, writeTimeoutSecond);
|
||||
|
||||
public HttpContext(HttpContextConfig config) {
|
||||
super(config);
|
||||
random.setSeed(Math.abs(System.nanoTime()));
|
||||
}
|
||||
|
||||
@@ -57,6 +49,21 @@ public class HttpContext extends Context {
|
||||
return responsePool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Consumer<ByteBuffer> getBufferConsumer() {
|
||||
return super.getBufferConsumer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer buffer) {
|
||||
super.offerBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
super.offerBuffer(buffers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
|
||||
@@ -72,6 +79,7 @@ public class HttpContext extends Context {
|
||||
//生成规则与SncpAsyncHandler.Factory 很类似
|
||||
//-------------------------------------------------------------
|
||||
final boolean handlerinterface = handlerClass.isInterface();
|
||||
final String cpDesc = Type.getDescriptor(ConstructorParameters.class);
|
||||
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||
final String handlerName = CompletionHandler.class.getName().replace('.', '/');
|
||||
final String handlerDesc = Type.getDescriptor(CompletionHandler.class);
|
||||
@@ -91,7 +99,7 @@ public class HttpContext extends Context {
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
|
||||
//mv.setDebug(true);
|
||||
{
|
||||
av0 = mv.visitAnnotation("Lorg/redkale/util/ConstructorParameters;", true);
|
||||
av0 = mv.visitAnnotation(cpDesc, true);
|
||||
{
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
av1.visit(null, "handler");
|
||||
@@ -168,4 +176,8 @@ public class HttpContext extends Context {
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
return (Creator<H>) Creator.create(newHandlerClazz);
|
||||
}
|
||||
|
||||
public static class HttpContextConfig extends ContextConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
List<HttpServlet> list = removeHttpServlet(predicateEntry, predicateFilter);
|
||||
return list == null || list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends WebSocket> HttpServlet removeHttpServlet(Class<T> websocketOrServletType) {
|
||||
Predicate<MappingEntry> predicateEntry = (t) -> {
|
||||
Class type = t.servlet.getClass();
|
||||
@@ -144,6 +144,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
return list == null || list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean addForbidURIReg(final String urlreg) {
|
||||
if (urlreg == null || urlreg.isEmpty()) return false;
|
||||
synchronized (excludeLock) {
|
||||
@@ -169,6 +170,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean removeForbidURIReg(final String urlreg) {
|
||||
if (urlreg == null || urlreg.isEmpty()) return false;
|
||||
synchronized (excludeLock) {
|
||||
@@ -198,6 +200,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void init(HttpContext context, AnyValue config) {
|
||||
super.init(context, config); //必须要执行
|
||||
Collection<HttpServlet> servlets = getServlets();
|
||||
@@ -225,7 +228,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
|
||||
try {
|
||||
this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).newInstance();
|
||||
this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).getDeclaredConstructor().newInstance();
|
||||
} catch (Throwable e) {
|
||||
this.resourceHttpServlet = new HttpResourceServlet();
|
||||
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
|
||||
@@ -239,7 +242,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
for (AnyValue renderConfig : renderConfigs) {
|
||||
String renderType = renderConfig.getValue("value");
|
||||
try {
|
||||
HttpRender render = (HttpRender) Thread.currentThread().getContextClassLoader().loadClass(renderType).newInstance();
|
||||
HttpRender render = (HttpRender) Thread.currentThread().getContextClassLoader().loadClass(renderType).getDeclaredConstructor().newInstance();
|
||||
for (HttpRender one : renders) {
|
||||
if (one.getType().equals(render.getType())) throw new RuntimeException("HttpRender(" + renderType + ") repeat");
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.*;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.*;
|
||||
@@ -31,8 +31,6 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
*/
|
||||
public class HttpRequest extends Request<HttpContext> {
|
||||
|
||||
protected static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
public static final String SESSIONID_NAME = "JSESSIONID";
|
||||
|
||||
@Comment("Method GET/POST/...")
|
||||
@@ -134,15 +132,19 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
String value = array.toString(index, array.size() - index, charset).trim();
|
||||
switch (name) {
|
||||
case "Content-Type":
|
||||
case "content-type":
|
||||
this.contentType = value;
|
||||
break;
|
||||
case "Content-Length":
|
||||
case "content-length":
|
||||
this.contentLength = Long.decode(value);
|
||||
break;
|
||||
case "Host":
|
||||
case "host":
|
||||
this.host = value;
|
||||
break;
|
||||
case "Cookie":
|
||||
case "cookie":
|
||||
if (this.cookie == null || this.cookie.isEmpty()) {
|
||||
this.cookie = value;
|
||||
} else {
|
||||
@@ -150,8 +152,14 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
break;
|
||||
case "Connection":
|
||||
case "connection":
|
||||
this.connection = value;
|
||||
if (context.getAliveTimeoutSeconds() >= 0) {
|
||||
this.setKeepAlive(!"close".equalsIgnoreCase(value));
|
||||
}
|
||||
break;
|
||||
case "user-agent":
|
||||
header.addValue("User-Agent", value);
|
||||
break;
|
||||
default:
|
||||
header.addValue(name, value);
|
||||
@@ -332,7 +340,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* @return 内容
|
||||
*/
|
||||
public String getBodyUTF8() {
|
||||
return array.toString(UTF8);
|
||||
return array.toString(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,7 +352,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* @return 内容
|
||||
*/
|
||||
public <T> T getBodyJson(java.lang.reflect.Type type) {
|
||||
String str = array.toString(UTF8);
|
||||
String str = array.toString(StandardCharsets.UTF_8);
|
||||
if (str == null || str.isEmpty()) return null;
|
||||
return context.getJsonConvert().convertFrom(type, str);
|
||||
}
|
||||
@@ -359,7 +367,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* @return 内容
|
||||
*/
|
||||
public <T> T getBodyJson(JsonConvert convert, java.lang.reflect.Type type) {
|
||||
String str = array.toString(UTF8);
|
||||
String str = array.toString(StandardCharsets.UTF_8);
|
||||
if (str == null || str.isEmpty()) return null;
|
||||
return convert.convertFrom(type, str);
|
||||
}
|
||||
@@ -626,7 +634,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
public short getRequstURILastPath(short defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
if (val.isEmpty()) return defvalue;
|
||||
return val.isEmpty() ? defvalue : Short.parseShort(val);
|
||||
try {
|
||||
return Short.parseShort(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -642,7 +654,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
public short getRequstURILastPath(int radix, short defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
if (val.isEmpty()) return defvalue;
|
||||
return val.isEmpty() ? defvalue : Short.parseShort(val, radix);
|
||||
try {
|
||||
return Short.parseShort(val, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -656,7 +672,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public int getRequstURILastPath(int defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
try {
|
||||
return val.isEmpty() ? defvalue : Integer.parseInt(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,7 +691,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public int getRequstURILastPath(int radix, int defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
try {
|
||||
return val.isEmpty() ? defvalue : Integer.parseInt(val, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -685,7 +709,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public float getRequstURILastPath(float defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
try {
|
||||
return val.isEmpty() ? defvalue : Float.parseFloat(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,7 +727,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public long getRequstURILastPath(long defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
try {
|
||||
return val.isEmpty() ? defvalue : Long.parseLong(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -714,7 +746,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public long getRequstURILastPath(int radix, long defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
try {
|
||||
return val.isEmpty() ? defvalue : Long.parseLong(val, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -728,7 +764,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public double getRequstURILastPath(double defvalue) {
|
||||
String val = getRequstURILastPath();
|
||||
try {
|
||||
return val.isEmpty() ? defvalue : Double.parseDouble(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -775,7 +815,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public short getRequstURIPath(String prefix, short defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Short.parseShort(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -791,7 +835,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public short getRequstURIPath(int radix, String prefix, short defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Short.parseShort(val, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,7 +855,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public int getRequstURIPath(String prefix, int defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Integer.parseInt(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -824,7 +876,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public int getRequstURIPath(int radix, String prefix, int defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Integer.parseInt(val, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -839,7 +895,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public float getRequstURIPath(String prefix, float defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Float.parseFloat(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -854,7 +914,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public long getRequstURIPath(String prefix, long defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Long.parseLong(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -870,7 +934,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public long getRequstURIPath(int radix, String prefix, long defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Long.parseLong(val, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,7 +953,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*/
|
||||
public double getRequstURIPath(String prefix, double defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
try {
|
||||
return val == null ? defvalue : Double.parseDouble(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return defvalue;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -1422,10 +1494,13 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
|
||||
if (flipper == null) {
|
||||
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||
int limit = getRequstURIPath("limit:", maxLimit);
|
||||
int limit = getRequstURIPath("limit:", 0);
|
||||
int offset = getRequstURIPath("offset:", 0);
|
||||
String sort = getRequstURIPath("sort:", "");
|
||||
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, sort);
|
||||
if (limit > 0) {
|
||||
if (limit > maxLimit) limit = maxLimit;
|
||||
flipper = new org.redkale.source.Flipper(limit, offset, sort);
|
||||
}
|
||||
} else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) {
|
||||
flipper.setLimit(maxLimit);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
final String uri = path.toString().substring(rootstr.length()).replace('\\', '/');
|
||||
//logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event");
|
||||
if (event.kind() == ENTRY_DELETE) {
|
||||
files.remove(uri);
|
||||
FileEntry en = files.remove(uri);
|
||||
if (en != null) en.remove();
|
||||
} else if (event.kind() == ENTRY_MODIFY) {
|
||||
FileEntry en = files.get(uri);
|
||||
if (en != null && en.file != null) {
|
||||
@@ -317,10 +318,8 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
public void remove() {
|
||||
if (this.content != null) this.servlet.cachedLength.add(0L - this.content.remaining());
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
public long getCachedLength() {
|
||||
|
||||
@@ -11,7 +11,8 @@ import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.file.*;
|
||||
import java.text.*;
|
||||
import java.time.ZoneId;
|
||||
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -39,19 +40,22 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
private static final ByteBuffer buffer404 = ByteBuffer.wrap("HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer();
|
||||
|
||||
protected static final byte[] status200Bytes = "HTTP/1.1 200 OK\r\n".getBytes();
|
||||
|
||||
protected static final byte[] LINE = new byte[]{'\r', '\n'};
|
||||
|
||||
protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes();
|
||||
|
||||
private static final Set<OpenOption> options = new HashSet<>();
|
||||
protected static final byte[] connectCloseBytes = "Connection: close\r\n".getBytes();
|
||||
|
||||
private static final DateFormat GMT_DATE_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH);
|
||||
protected static final byte[] connectAliveBytes = "Connection: keep-alive\r\n".getBytes();
|
||||
|
||||
private static final Set<OpenOption> options = new HashSet<>();
|
||||
|
||||
private static final Map<Integer, String> httpCodes = new HashMap<>();
|
||||
|
||||
static {
|
||||
options.add(StandardOpenOption.READ);
|
||||
GMT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
httpCodes.put(100, "Continue");
|
||||
httpCodes.put(101, "Switching Protocols");
|
||||
@@ -99,9 +103,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
httpCodes.put(505, "HTTP Version Not Supported");
|
||||
}
|
||||
|
||||
private static final ZoneId ZONE_GMT = ZoneId.of("GMT");
|
||||
|
||||
private int status = 200;
|
||||
|
||||
private String contentType = "text/plain; charset=utf-8";
|
||||
private String contentType = "";
|
||||
|
||||
private long contentLength = -1;
|
||||
|
||||
@@ -112,6 +118,14 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
private BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> bufferHandler;
|
||||
//------------------------------------------------
|
||||
|
||||
private final String plainContentType;
|
||||
|
||||
private final byte[] plainContentTypeBytes;
|
||||
|
||||
private final String jsonContentType;
|
||||
|
||||
private final byte[] jsonContentTypeBytes;
|
||||
|
||||
private final DefaultAnyValue header = new DefaultAnyValue();
|
||||
|
||||
private final String[][] defaultAddHeaders;
|
||||
@@ -120,7 +134,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
private final boolean autoOptions;
|
||||
|
||||
private final HttpCookie defcookie;
|
||||
private final Supplier<byte[]> dateSupplier;
|
||||
|
||||
private final HttpCookie defaultCookie;
|
||||
|
||||
private final List<HttpRender> renders;
|
||||
|
||||
@@ -132,16 +148,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle());
|
||||
}
|
||||
|
||||
public HttpResponse(HttpContext context, HttpRequest request, String[][] defaultAddHeaders, String[][] defaultSetHeaders,
|
||||
HttpCookie defcookie, boolean autoOptions, List< HttpRender> renders) {
|
||||
public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) {
|
||||
super(context, request);
|
||||
this.defaultAddHeaders = defaultAddHeaders;
|
||||
this.defaultSetHeaders = defaultSetHeaders;
|
||||
this.defcookie = defcookie;
|
||||
this.autoOptions = autoOptions;
|
||||
this.renders = renders;
|
||||
this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
|
||||
this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
|
||||
this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
|
||||
this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes();
|
||||
this.defaultAddHeaders = config.defaultAddHeaders;
|
||||
this.defaultSetHeaders = config.defaultSetHeaders;
|
||||
this.defaultCookie = config.defaultCookie;
|
||||
this.autoOptions = config.autoOptions;
|
||||
this.dateSupplier = config.dateSupplier;
|
||||
this.renders = config.renders;
|
||||
this.hasRender = renders != null && !renders.isEmpty();
|
||||
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
|
||||
this.contentType = this.plainContentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,7 +176,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
@Override
|
||||
protected boolean recycle() {
|
||||
boolean rs = super.recycle();
|
||||
this.status = 200;
|
||||
this.contentLength = -1;
|
||||
this.contentType = null;
|
||||
@@ -163,7 +183,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
this.headsended = false;
|
||||
this.header.clear();
|
||||
this.bufferHandler = null;
|
||||
return rs;
|
||||
return super.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,6 +221,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
return this.autoOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
super.offerBuffer(buffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加Cookie值
|
||||
*
|
||||
@@ -270,7 +295,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
public void finishJson(final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), obj));
|
||||
}
|
||||
@@ -282,7 +307,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param objs 输出对象
|
||||
*/
|
||||
public void finishMapJson(final Object... objs) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = objs;
|
||||
finish(request.getJsonConvert().convertMapTo(getBodyBufferSupplier(), objs));
|
||||
}
|
||||
@@ -294,7 +319,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
finish(convert.convertTo(getBodyBufferSupplier(), obj));
|
||||
}
|
||||
@@ -307,7 +332,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param objs 输出对象
|
||||
*/
|
||||
public void finishMapJson(final JsonConvert convert, final Object... objs) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = objs;
|
||||
finish(convert.convertMapTo(getBodyBufferSupplier(), objs));
|
||||
}
|
||||
@@ -319,7 +344,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
public void finishJson(final Type type, final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
this.output = obj;
|
||||
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), type, obj));
|
||||
}
|
||||
@@ -332,7 +357,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
finish(convert.convertTo(getBodyBufferSupplier(), type, obj));
|
||||
}
|
||||
@@ -343,7 +368,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param objs 输出对象
|
||||
*/
|
||||
public void finishJson(final Object... objs) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = objs;
|
||||
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), objs));
|
||||
}
|
||||
@@ -354,7 +379,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param ret RetResult输出对象
|
||||
*/
|
||||
public void finishJson(final org.redkale.service.RetResult ret) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = ret;
|
||||
if (ret != null && !ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||
@@ -370,7 +395,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param ret RetResult输出对象
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = ret;
|
||||
if (ret != null && !ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||
@@ -473,6 +498,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
|
||||
if (result.getResult() == null) {
|
||||
finish("");
|
||||
} else if (result.getResult() instanceof CharSequence) {
|
||||
finish(result.getResult().toString());
|
||||
} else {
|
||||
finish(convert, result.getResult());
|
||||
}
|
||||
@@ -493,8 +520,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (convert instanceof TextConvert) this.contentType = "text/plain; charset=utf-8";
|
||||
if (convert instanceof JsonConvert) {
|
||||
this.contentType = this.jsonContentType;
|
||||
} else if (convert instanceof TextConvert) {
|
||||
this.contentType = this.plainContentType;
|
||||
}
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
if (obj instanceof org.redkale.service.RetResult) {
|
||||
org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj;
|
||||
@@ -586,7 +616,26 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
public void finish(final byte[] bs) {
|
||||
if (isClosed()) return; //避免重复关闭
|
||||
if (this.context.getBufferCapacity() >= bs.length) {
|
||||
ByteBuffer buffer = this.context.pollBuffer();
|
||||
ByteBuffer buffer = getBodyBufferSupplier().get();
|
||||
buffer.put(bs);
|
||||
buffer.flip();
|
||||
this.finish(false, buffer);
|
||||
} else {
|
||||
this.finish(false, ByteBuffer.wrap(bs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定byte[]按响应结果输出
|
||||
*
|
||||
* @param contentType ContentType
|
||||
* @param bs 输出内容
|
||||
*/
|
||||
public void finish(final String contentType, final byte[] bs) {
|
||||
if (isClosed()) return; //避免重复关闭
|
||||
this.contentType = contentType;
|
||||
if (this.context.getBufferCapacity() >= bs.length) {
|
||||
ByteBuffer buffer = getBodyBufferSupplier().get();
|
||||
buffer.put(bs);
|
||||
buffer.flip();
|
||||
this.finish(false, buffer);
|
||||
@@ -832,17 +881,22 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
protected ByteBuffer createHeader() {
|
||||
this.headsended = true;
|
||||
ByteBuffer buffer = this.pollWriteReadBuffer();
|
||||
buffer.put(("HTTP/1.1 " + this.status + " " + (this.status == 200 ? "OK" : httpCodes.get(this.status)) + "\r\n").getBytes());
|
||||
|
||||
buffer.put(("Content-Type: " + (this.contentType == null ? "text/plain; charset=utf-8" : this.contentType) + "\r\n").getBytes());
|
||||
|
||||
if (this.contentLength >= 0) {
|
||||
buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
|
||||
if (this.status == 200) {
|
||||
buffer.put(status200Bytes);
|
||||
} else {
|
||||
buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes());
|
||||
}
|
||||
if (!this.request.isKeepAlive()) {
|
||||
buffer.put("Connection: close\r\n".getBytes());
|
||||
if (this.contentLength >= 0) buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
|
||||
if (this.contentType == this.jsonContentType) {
|
||||
buffer.put(this.jsonContentTypeBytes);
|
||||
} else if (this.contentType == null || this.contentType == this.plainContentType) {
|
||||
buffer.put(this.plainContentTypeBytes);
|
||||
} else {
|
||||
buffer.put(("Content-Type: " + (this.contentType == null ? this.plainContentType : this.contentType) + "\r\n").getBytes());
|
||||
}
|
||||
buffer.put(serverNameBytes);
|
||||
if (dateSupplier != null) buffer.put(dateSupplier.get());
|
||||
buffer.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes);
|
||||
|
||||
if (this.defaultAddHeaders != null) {
|
||||
for (String[] headers : this.defaultAddHeaders) {
|
||||
@@ -874,13 +928,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
|
||||
}
|
||||
if (request.newsessionid != null) {
|
||||
String domain = defcookie == null ? null : defcookie.getDomain();
|
||||
String domain = defaultCookie == null ? null : defaultCookie.getDomain();
|
||||
if (domain == null) {
|
||||
domain = "";
|
||||
} else {
|
||||
domain = "Domain=" + domain + "; ";
|
||||
}
|
||||
String path = defcookie == null ? null : defcookie.getPath();
|
||||
String path = defaultCookie == null ? null : defaultCookie.getPath();
|
||||
if (path == null || path.isEmpty()) path = "/";
|
||||
if (request.newsessionid.isEmpty()) {
|
||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes());
|
||||
@@ -891,9 +945,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
if (this.cookies != null) {
|
||||
for (HttpCookie cookie : this.cookies) {
|
||||
if (cookie == null) continue;
|
||||
if (defcookie != null) {
|
||||
if (defcookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defcookie.getDomain());
|
||||
if (defcookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defcookie.getPath());
|
||||
if (defaultCookie != null) {
|
||||
if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain());
|
||||
if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath());
|
||||
}
|
||||
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
|
||||
}
|
||||
@@ -910,9 +964,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist());
|
||||
if (cookie.getMaxAge() > 0) {
|
||||
sb.append("; Max-Age=").append(cookie.getMaxAge());
|
||||
synchronized (GMT_DATE_FORMAT) {
|
||||
sb.append("; Expires=").append(GMT_DATE_FORMAT.format(new Date(System.currentTimeMillis() + cookie.getMaxAge() * 1000)));
|
||||
}
|
||||
sb.append("; Expires=").append(RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(ZONE_GMT).plusSeconds(cookie.getMaxAge())));
|
||||
}
|
||||
if (cookie.getSecure()) sb.append("; Secure");
|
||||
if (cookie.isHttpOnly()) sb.append("; HttpOnly");
|
||||
@@ -1142,4 +1194,28 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class HttpResponseConfig {
|
||||
|
||||
public String plainContentType;
|
||||
|
||||
public String jsonContentType;
|
||||
|
||||
public String[][] defaultAddHeaders;
|
||||
|
||||
public String[][] defaultSetHeaders;
|
||||
|
||||
public HttpCookie defaultCookie;
|
||||
|
||||
public boolean autoOptions;
|
||||
|
||||
public Supplier<byte[]> dateSupplier;
|
||||
|
||||
public List< HttpRender> renders;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ public class HttpResult<T> {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public HttpResult(String contentType, T result) {
|
||||
this.contentType = contentType;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public HttpResult<T> header(String name, Serializable value) {
|
||||
if (this.headers == null) this.headers = new HashMap<>();
|
||||
this.headers.put(name, String.valueOf(value));
|
||||
|
||||
@@ -59,10 +59,12 @@ public class HttpScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T find(String name) {
|
||||
return this.attributes == null ? null : (T) this.attributes.get(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T find(HttpScope parent, String name) {
|
||||
T rs = this.attributes == null ? null : (T) this.attributes.get(name);
|
||||
if (rs != null) return rs;
|
||||
|
||||
@@ -8,10 +8,17 @@ package org.redkale.net.http;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.HttpCookie;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.*;
|
||||
import java.time.ZoneId;
|
||||
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.HttpContext.HttpContextConfig;
|
||||
import org.redkale.net.http.HttpResponse.HttpResponseConfig;
|
||||
import org.redkale.net.sncp.Sncp;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.util.*;
|
||||
@@ -26,6 +33,10 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpResponse, HttpServlet> {
|
||||
|
||||
private ScheduledThreadPoolExecutor dateScheduler;
|
||||
|
||||
private byte[] currDateBytes;
|
||||
|
||||
public HttpServer() {
|
||||
this(System.currentTimeMillis(), ResourceFactory.root());
|
||||
}
|
||||
@@ -43,6 +54,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
super.init(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(final AnyValue config) throws Exception {
|
||||
super.destroy(config);
|
||||
if (this.dateScheduler != null) {
|
||||
this.dateScheduler.shutdownNow();
|
||||
this.dateScheduler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<HttpServlet> getHttpServlets() {
|
||||
return this.prepare.getServlets();
|
||||
}
|
||||
@@ -295,7 +315,9 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
final List<String[]> defaultAddHeaders = new ArrayList<>();
|
||||
final List<String[]> defaultSetHeaders = new ArrayList<>();
|
||||
boolean autoOptions = false;
|
||||
|
||||
int datePeriod = 0;
|
||||
String plainContentType = null;
|
||||
String jsonContentType = null;
|
||||
HttpCookie defaultCookie = null;
|
||||
String remoteAddrHeader = null;
|
||||
if (config != null) {
|
||||
@@ -314,6 +336,11 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
|
||||
AnyValue resps = config == null ? null : config.getAnyValue("response");
|
||||
if (resps != null) {
|
||||
AnyValue contenttypes = resps.getAnyValue("contenttype");
|
||||
if (contenttypes != null) {
|
||||
plainContentType = contenttypes.getValue("plain");
|
||||
jsonContentType = contenttypes.getValue("json");
|
||||
}
|
||||
AnyValue[] addHeaders = resps.getAnyValues("addheader");
|
||||
if (addHeaders.length > 0) {
|
||||
for (AnyValue addHeader : addHeaders) {
|
||||
@@ -360,22 +387,74 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
}
|
||||
AnyValue options = resps == null ? null : resps.getAnyValue("options");
|
||||
autoOptions = options != null && options.getBoolValue("auto", false);
|
||||
|
||||
AnyValue dates = resps == null ? null : resps.getAnyValue("date");
|
||||
datePeriod = dates == null ? 0 : dates.getIntValue("period", 0);
|
||||
}
|
||||
|
||||
}
|
||||
final String[][] addHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
|
||||
final String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
|
||||
final boolean options = autoOptions;
|
||||
Supplier<byte[]> dateSupplier = null;
|
||||
if (datePeriod == 0) {
|
||||
final ZoneId gmtZone = ZoneId.of("GMT");
|
||||
dateSupplier = () -> ("Date: " + RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(gmtZone)) + "\r\n").getBytes();
|
||||
} else if (datePeriod > 0) {
|
||||
if (this.dateScheduler == null) {
|
||||
this.dateScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, "HTTP:" + port + "-DateSchedule-Thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
final DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM y HH:mm:ss z", Locale.ENGLISH);
|
||||
gmtDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes();
|
||||
final int dp = datePeriod;
|
||||
this.dateScheduler.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes();
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "HttpServer schedule(interval=" + dp + "ms) date-format error", t);
|
||||
}
|
||||
}, 1000 - System.currentTimeMillis() % 1000, datePeriod, TimeUnit.MILLISECONDS);
|
||||
dateSupplier = () -> currDateBytes;
|
||||
}
|
||||
}
|
||||
|
||||
final HttpCookie defCookie = defaultCookie;
|
||||
final String addrHeader = remoteAddrHeader;
|
||||
|
||||
final HttpResponseConfig respConfig = new HttpResponseConfig();
|
||||
respConfig.plainContentType = plainContentType;
|
||||
respConfig.jsonContentType = jsonContentType;
|
||||
respConfig.defaultAddHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
|
||||
respConfig.defaultSetHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
|
||||
respConfig.defaultCookie = defaultCookie;
|
||||
respConfig.autoOptions = autoOptions;
|
||||
respConfig.dateSupplier = dateSupplier;
|
||||
respConfig.renders = ((HttpPrepareServlet) prepare).renders;
|
||||
|
||||
AtomicLong createResponseCounter = new AtomicLong();
|
||||
AtomicLong cycleResponseCounter = new AtomicLong();
|
||||
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
||||
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, this.sslContext, rcapacity, bufferPool, responsePool,
|
||||
this.maxbody, this.charset, this.address, this.resourceFactory, this.prepare, this.readTimeoutSecond, this.writeTimeoutSecond);
|
||||
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie, options, ((HttpPrepareServlet) prepare).renders));
|
||||
|
||||
final HttpContextConfig contextConfig = new HttpContextConfig();
|
||||
contextConfig.serverStartTime = this.serverStartTime;
|
||||
contextConfig.logger = this.logger;
|
||||
contextConfig.executor = this.executor;
|
||||
contextConfig.sslContext = this.sslContext;
|
||||
contextConfig.bufferCapacity = rcapacity;
|
||||
contextConfig.bufferPool = bufferPool;
|
||||
contextConfig.responsePool = responsePool;
|
||||
contextConfig.maxconns = this.maxconns;
|
||||
contextConfig.maxbody = this.maxbody;
|
||||
contextConfig.charset = this.charset;
|
||||
contextConfig.address = this.address;
|
||||
contextConfig.prepare = this.prepare;
|
||||
contextConfig.resourceFactory = this.resourceFactory;
|
||||
contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds;
|
||||
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
|
||||
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
|
||||
|
||||
HttpContext httpcontext = new HttpContext(contextConfig);
|
||||
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), respConfig));
|
||||
return httpcontext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
HttpServlet instance = (HttpServlet) newClazz.newInstance();
|
||||
HttpServlet instance = (HttpServlet) newClazz.getDeclaredConstructor().newInstance();
|
||||
instance.getClass().getField(factfield).set(instance, this);
|
||||
return instance;
|
||||
} catch (Exception ex) {
|
||||
@@ -294,7 +294,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
|
||||
this.ignore = mapping == null || !mapping.auth();
|
||||
this.cacheseconds = mapping == null ? 0 : mapping.cacheseconds();
|
||||
this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null;
|
||||
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
|
||||
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
||||
int status = response.getStatus();
|
||||
if (status != 200) return null;
|
||||
|
||||
@@ -70,6 +70,7 @@ public class MimeType {
|
||||
contentTypes.put("jpeg", "image/jpeg");
|
||||
contentTypes.put("jpg", "image/jpeg");
|
||||
contentTypes.put("js", "text/javascript");
|
||||
contentTypes.put("json", "application/json");
|
||||
contentTypes.put("kar", "audio/x-midi");
|
||||
contentTypes.put("latex", "application/x-latex");
|
||||
contentTypes.put("log", "text/plain");
|
||||
|
||||
@@ -26,8 +26,6 @@ public final class MultiContext {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MultiContext.class.getSimpleName());
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
private final String contentType;
|
||||
|
||||
private final InputStream in;
|
||||
@@ -58,7 +56,7 @@ public final class MultiContext {
|
||||
};
|
||||
|
||||
public MultiContext(final Charset charsetName, final String contentType, final DefaultAnyValue params, final InputStream in, String fielnameRegex) {
|
||||
this.charset = charsetName == null ? UTF8 : charsetName;
|
||||
this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName;
|
||||
this.contentType = contentType == null ? "" : contentType.trim();
|
||||
this.parameters = params;
|
||||
this.boundary = parseBoundary(this.contentType);
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.redkale.asm.Opcodes.*;
|
||||
import org.redkale.asm.Type;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.net.Cryptor;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.source.Flipper;
|
||||
@@ -95,7 +96,7 @@ public final class Rest {
|
||||
String key = name + ":" + desc;
|
||||
if (fieldmap.containsKey(key)) return null;
|
||||
fieldmap.put(key, fieldnames);
|
||||
return new MethodVisitor(Opcodes.ASM5) {
|
||||
return new MethodVisitor(Opcodes.ASM6) {
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
|
||||
if (index < 1) return;
|
||||
@@ -118,7 +119,7 @@ public final class Rest {
|
||||
InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class");
|
||||
if (in == null) return map;
|
||||
try {
|
||||
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM5, map), 0);
|
||||
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM6, map), 0);
|
||||
} catch (Exception e) { //无需理会
|
||||
}
|
||||
Class superClass = clazz.getSuperclass();
|
||||
@@ -129,7 +130,7 @@ public final class Rest {
|
||||
|
||||
static JsonConvert createJsonConvert(RestConvert[] converts) {
|
||||
if (converts == null || converts.length < 1) return JsonConvert.root();
|
||||
final JsonFactory childFactory = JsonFactory.root().createChild();
|
||||
final JsonFactory childFactory = JsonFactory.create();
|
||||
List<Class> types = new ArrayList<>();
|
||||
for (RestConvert rc : converts) {
|
||||
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
|
||||
@@ -151,14 +152,14 @@ public final class Rest {
|
||||
final RestService controller = serviceType.getAnnotation(RestService.class);
|
||||
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
||||
if (controller.ignore()) return null;
|
||||
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
||||
return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
||||
}
|
||||
|
||||
static String getWebModuleName(Class<? extends Service> serviceType) {
|
||||
final RestService controller = serviceType.getAnnotation(RestService.class);
|
||||
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "");
|
||||
if (controller.ignore()) return null;
|
||||
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "");
|
||||
return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", "");
|
||||
}
|
||||
|
||||
static boolean isRestDyn(HttpServlet servlet) {
|
||||
@@ -202,8 +203,8 @@ public final class Rest {
|
||||
}
|
||||
if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet");
|
||||
final String rwsname = ResourceFactory.formatResourceName(rws.name());
|
||||
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
|
||||
@@ -253,6 +254,10 @@ public final class Rest {
|
||||
messageMethods.add(method);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------
|
||||
final String resDesc = Type.getDescriptor(Resource.class);
|
||||
final String wsDesc = Type.getDescriptor(WebSocket.class);
|
||||
final String wsParamDesc = Type.getDescriptor(WebSocketParam.class);
|
||||
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
|
||||
final String convertDisabledDesc = Type.getDescriptor(ConvertDisabled.class);
|
||||
final String webSocketParamName = Type.getInternalName(WebSocketParam.class);
|
||||
final String supDynName = WebSocketServlet.class.getName().replace('.', '/');
|
||||
@@ -315,7 +320,7 @@ public final class Rest {
|
||||
java.lang.reflect.Type fieldType = field.getGenericType();
|
||||
fv = cw.visitField(ACC_PRIVATE, "_redkale_resource_" + i, Type.getDescriptor(field.getType()), fieldType == field.getType() ? null : Utility.getTypeDescriptor(fieldType), null);
|
||||
{
|
||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0 = fv.visitAnnotation(resDesc, true);
|
||||
av0.visit("name", res.name());
|
||||
av0.visitEnd();
|
||||
}
|
||||
@@ -355,7 +360,7 @@ public final class Rest {
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ //createWebSocket 方法
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()Lorg/redkale/net/http/WebSocket;", "<G::Ljava/io/Serializable;T:Ljava/lang/Object;>()Lorg/redkale/net/http/WebSocket<TG;TT;>;", null));
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()" + wsDesc, "<G::Ljava/io/Serializable;T:Ljava/lang/Object;>()L" + WebSocket.class.getName().replace('.', '/') + "<TG;TT;>;", null));
|
||||
mv.visitTypeInsn(NEW, newDynName + "$" + newDynWebSokcetSimpleName);
|
||||
mv.visitInsn(DUP);
|
||||
for (int i = 0; i < resourcesFields.size(); i++) {
|
||||
@@ -368,7 +373,7 @@ public final class Rest {
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ //createRestOnMessageConsumer
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;>;", null));
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", null));
|
||||
mv.visitTypeInsn(NEW, newDynConsumerFullName);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, newDynConsumerFullName, "<init>", "()V", false);
|
||||
@@ -475,7 +480,7 @@ public final class Rest {
|
||||
mv.visitLdcInsn(method.getAnnotation(RestOnMessage.class).name());
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, "preOnMessage", "(Ljava/lang/String;Lorg/redkale/net/http/WebSocketParam;Ljava/lang/Runnable;)V", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, "preOnMessage", "(Ljava/lang/String;" + wsParamDesc + "Ljava/lang/Runnable;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(4, 2);
|
||||
mv.visitEnd();
|
||||
@@ -497,9 +502,9 @@ public final class Rest {
|
||||
}
|
||||
{ //toString
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
|
||||
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
|
||||
mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/convert/json/JsonConvert", "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitEnd();
|
||||
@@ -532,9 +537,9 @@ public final class Rest {
|
||||
}
|
||||
{ //toString
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
|
||||
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
|
||||
mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/convert/json/JsonConvert", "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitEnd();
|
||||
@@ -569,7 +574,7 @@ public final class Rest {
|
||||
|
||||
{ //_DynRestOnMessageConsumer class
|
||||
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
|
||||
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"});
|
||||
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"});
|
||||
|
||||
cw2.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC);
|
||||
cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC);
|
||||
@@ -589,7 +594,7 @@ public final class Rest {
|
||||
}
|
||||
|
||||
{ //accept函数
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;)V", null, null));
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", null, null));
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, newDynWebSokcetFullName);
|
||||
mv.visitVarInsn(ASTORE, 3);
|
||||
@@ -623,10 +628,10 @@ public final class Rest {
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, "org/redkale/net/http/WebSocket");
|
||||
mv.visitTypeInsn(CHECKCAST, WebSocket.class.getName().replace('.', '/'));
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitTypeInsn(CHECKCAST, "java/lang/Object");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;)V", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
@@ -637,7 +642,14 @@ public final class Rest {
|
||||
cw.visitEnd();
|
||||
Class<?> newClazz = newLoader.loadClass(newDynName.replace('/', '.'), cw.toByteArray());
|
||||
try {
|
||||
return (T) newClazz.newInstance();
|
||||
T servlet = (T) newClazz.getDeclaredConstructor().newInstance();
|
||||
if (rws.cryptor() != Cryptor.class) {
|
||||
Cryptor cryptor = rws.cryptor().getDeclaredConstructor().newInstance();
|
||||
Field cryptorField = newClazz.getSuperclass().getDeclaredField("cryptor"); //WebSocketServlet
|
||||
cryptorField.setAccessible(true);
|
||||
cryptorField.set(servlet, cryptor);
|
||||
}
|
||||
return servlet;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -652,6 +664,7 @@ public final class Rest {
|
||||
|
||||
final String serviceDesc = Type.getDescriptor(serviceType);
|
||||
final String webServletDesc = Type.getDescriptor(WebServlet.class);
|
||||
final String resDesc = Type.getDescriptor(Resource.class);
|
||||
final String reqDesc = Type.getDescriptor(HttpRequest.class);
|
||||
final String respDesc = Type.getDescriptor(HttpResponse.class);
|
||||
final String convertDesc = Type.getDescriptor(Convert.class);
|
||||
@@ -662,6 +675,8 @@ public final class Rest {
|
||||
final String flipperDesc = Type.getDescriptor(Flipper.class);
|
||||
final String httprsDesc = Type.getDescriptor(HttpResult.class);
|
||||
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
|
||||
final String multiContextDesc = Type.getDescriptor(MultiContext.class);
|
||||
final String multiContextName = MultiContext.class.getName().replace('.', '/');
|
||||
final String mappingDesc = Type.getDescriptor(HttpMapping.class);
|
||||
final String webparamDesc = Type.getDescriptor(HttpParam.class);
|
||||
final String webparamsDesc = Type.getDescriptor(HttpParam.HttpParams.class);
|
||||
@@ -686,8 +701,8 @@ public final class Rest {
|
||||
final String defmodulename = getWebModuleNameLowerCase(serviceType);
|
||||
final String bigmodulename = getWebModuleName(serviceType);
|
||||
final String catalog = controller == null ? "" : controller.catalog();
|
||||
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
MethodDebugVisitor mv;
|
||||
@@ -705,15 +720,83 @@ public final class Rest {
|
||||
av0.visit("value", Type.getType(Type.getDescriptor(serviceType)));
|
||||
av0.visitEnd();
|
||||
}
|
||||
|
||||
final List<MappingEntry> entrys = new ArrayList<>();
|
||||
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
|
||||
//获取所有可以转换成HttpMapping的方法
|
||||
int methodidex = 0;
|
||||
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
|
||||
for (final Method method : serviceType.getMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())) continue;
|
||||
if (method.isSynthetic()) continue;
|
||||
if (EXCLUDERMETHODS.contains(method.getName())) continue;
|
||||
if ("init".equals(method.getName())) continue;
|
||||
if ("destroy".equals(method.getName())) continue;
|
||||
if ("version".equals(method.getName())) continue;
|
||||
if (controller == null) continue;
|
||||
|
||||
RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class);
|
||||
if (!controller.automapping() && mappings.length < 1) continue;
|
||||
boolean ignore = false;
|
||||
for (RestMapping mapping : mappings) {
|
||||
if (mapping.ignore()) {
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ignore) continue;
|
||||
|
||||
Class[] extypes = method.getExceptionTypes();
|
||||
if (extypes.length > 1) {
|
||||
if (mappings != null && mappings.length > 0) throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method with throws IOException");
|
||||
continue;
|
||||
}
|
||||
if (extypes.length == 1 && extypes[0] != IOException.class) {
|
||||
if (mappings != null && mappings.length > 0) throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method with throws IOException");
|
||||
continue;
|
||||
}
|
||||
paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType));
|
||||
if (mappings.length == 0) { //没有Mapping,设置一个默认值
|
||||
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
|
||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||
entrys.add(entry);
|
||||
} else {
|
||||
for (RestMapping mapping : mappings) {
|
||||
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
|
||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||
entrys.add(entry);
|
||||
}
|
||||
}
|
||||
methodidex++;
|
||||
}
|
||||
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
|
||||
|
||||
{ //注入 @WebServlet 注解
|
||||
String urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*";
|
||||
String urlpath = "";
|
||||
int moduleid = controller == null ? 0 : controller.moduleid();
|
||||
boolean repair = controller == null ? true : controller.repair();
|
||||
String comment = controller == null ? "" : controller.comment();
|
||||
av0 = cw.visitAnnotation(webServletDesc, true);
|
||||
{
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
boolean pound = false;
|
||||
for (MappingEntry entry : entrys) {
|
||||
if (entry.existsPound) {
|
||||
pound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (defmodulename.isEmpty() || (!pound && entrys.size() <= 6)) {
|
||||
for (MappingEntry entry : entrys) {
|
||||
String suburl = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name;
|
||||
urlpath += "," + suburl;
|
||||
av1.visit(null, suburl);
|
||||
}
|
||||
if (urlpath.length() > 0) urlpath = urlpath.substring(1);
|
||||
} else {
|
||||
urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*";
|
||||
av1.visit(null, urlpath);
|
||||
}
|
||||
av1.visitEnd();
|
||||
}
|
||||
av0.visit("moduleid", moduleid);
|
||||
@@ -729,14 +812,14 @@ public final class Rest {
|
||||
|
||||
{ //注入 @Resource private XXXService _service;
|
||||
fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null);
|
||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0 = fv.visitAnnotation(resDesc, true);
|
||||
av0.visit("name", "");
|
||||
av0.visitEnd();
|
||||
fv.visitEnd();
|
||||
}
|
||||
{ //注入 @Resource(name = "APP_HOME") private File _redkale_home;
|
||||
fv = cw.visitField(ACC_PRIVATE, "_redkale_home", Type.getDescriptor(File.class), null, null);
|
||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0 = fv.visitAnnotation(resDesc, true);
|
||||
av0.visit("name", "APP_HOME");
|
||||
av0.visitEnd();
|
||||
fv.visitEnd();
|
||||
@@ -759,48 +842,6 @@ public final class Rest {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
final List<MappingEntry> entrys = new ArrayList<>();
|
||||
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
|
||||
//获取所有可以转换成HttpMapping的方法
|
||||
int methodidex = 0;
|
||||
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
|
||||
for (final Method method : serviceType.getMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())) continue;
|
||||
if (method.isSynthetic()) continue;
|
||||
Class[] extypes = method.getExceptionTypes();
|
||||
if (extypes.length > 1) continue;
|
||||
if (extypes.length == 1 && extypes[0] != IOException.class) continue;
|
||||
if (EXCLUDERMETHODS.contains(method.getName())) continue;
|
||||
if ("init".equals(method.getName())) continue;
|
||||
if ("destroy".equals(method.getName())) continue;
|
||||
if ("version".equals(method.getName())) continue;
|
||||
|
||||
RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class);
|
||||
if (controller == null) continue;
|
||||
if (!controller.automapping() && mappings.length < 1) continue;
|
||||
boolean ignore = false;
|
||||
for (RestMapping mapping : mappings) {
|
||||
if (mapping.ignore()) {
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ignore) continue;
|
||||
paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType));
|
||||
if (mappings.length == 0) { //没有Mapping,设置一个默认值
|
||||
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
|
||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||
entrys.add(entry);
|
||||
} else {
|
||||
for (RestMapping mapping : mappings) {
|
||||
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
|
||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||
entrys.add(entry);
|
||||
}
|
||||
}
|
||||
methodidex++;
|
||||
}
|
||||
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
|
||||
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
|
||||
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType);
|
||||
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
|
||||
@@ -977,7 +1018,7 @@ public final class Rest {
|
||||
}
|
||||
}
|
||||
av0 = mv.visitAnnotation(mappingDesc, true);
|
||||
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/" + entry.name + (reqpath ? "/" : "");
|
||||
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : "");
|
||||
av0.visit("url", url);
|
||||
av0.visit("auth", entry.auth);
|
||||
av0.visit("cacheseconds", entry.cacheseconds);
|
||||
@@ -1028,33 +1069,33 @@ public final class Rest {
|
||||
if (mupload != null) { //存在文件上传
|
||||
if (muploadType == byte[].class) {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false);
|
||||
mv.visitLdcInsn(mupload.maxLength());
|
||||
mv.visitLdcInsn(mupload.fileNameReg());
|
||||
mv.visitLdcInsn(mupload.contentTypeReg());
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
uploadLocal = maxLocals;
|
||||
} else if (muploadType == File.class) {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
|
||||
mv.visitLdcInsn(mupload.maxLength());
|
||||
mv.visitLdcInsn(mupload.fileNameReg());
|
||||
mv.visitLdcInsn(mupload.contentTypeReg());
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
uploadLocal = maxLocals;
|
||||
} else if (muploadType == File[].class) { //File[]
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
|
||||
mv.visitLdcInsn(mupload.maxLength());
|
||||
mv.visitLdcInsn(mupload.fileNameReg());
|
||||
mv.visitLdcInsn(mupload.contentTypeReg());
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
uploadLocal = maxLocals;
|
||||
}
|
||||
@@ -1684,7 +1725,7 @@ public final class Rest {
|
||||
cw.visitEnd();
|
||||
Class<?> newClazz = new RestClassLoader(loader).loadClass(newDynName.replace('/', '.'), cw.toByteArray());
|
||||
try {
|
||||
T obj = ((Class<T>) newClazz).newInstance();
|
||||
T obj = ((Class<T>) newClazz).getDeclaredConstructor().newInstance();
|
||||
for (Map.Entry<String, org.redkale.util.Attribute> en : restAttributes.entrySet()) {
|
||||
Field attrField = newClazz.getDeclaredField(en.getKey());
|
||||
attrField.setAccessible(true);
|
||||
@@ -1787,6 +1828,22 @@ public final class Rest {
|
||||
this.actionid = mapping.actionid();
|
||||
this.cacheseconds = mapping.cacheseconds();
|
||||
this.comment = mapping.comment();
|
||||
boolean pound = false;
|
||||
Parameter[] params = method.getParameters();
|
||||
for (Parameter param : params) {
|
||||
RestParam rp = param.getAnnotation(RestParam.class);
|
||||
if (rp != null && !rp.name().isEmpty() && rp.name().charAt(0) == '#') {
|
||||
pound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pound && params.length == 1) {
|
||||
Class ptype = method.getParameterTypes()[0];
|
||||
if (this.name.startsWith("find") || this.name.startsWith("delete")) {
|
||||
if (ptype.isPrimitive() || ptype == String.class) pound = true;
|
||||
}
|
||||
}
|
||||
this.existsPound = pound;
|
||||
}
|
||||
|
||||
public final int methodidx; // _paramtypes 的下标,从0开始
|
||||
@@ -1807,6 +1864,8 @@ public final class Rest {
|
||||
|
||||
public final int cacheseconds;
|
||||
|
||||
public final boolean existsPound; //是否包含#的参数
|
||||
|
||||
@RestMapping()
|
||||
void mapping() { //用于获取Mapping 默认值
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
|
||||
/**
|
||||
* 只能依附在Service实现类的public方法上 <br>
|
||||
* 只能依附在Service实现类的public方法上,且方法如果throws只能是IOException <br>
|
||||
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。 <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.net.http;
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import org.redkale.net.Cryptor;
|
||||
|
||||
/**
|
||||
* 只能依附在WebSocket类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl,的默认路径为 hello)。 <br>
|
||||
@@ -66,6 +67,13 @@ public @interface RestWebSocket {
|
||||
*/
|
||||
int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
|
||||
|
||||
/**
|
||||
* 加密解密器
|
||||
*
|
||||
* @return Cryptor
|
||||
*/
|
||||
Class<? extends Cryptor> cryptor() default Cryptor.class;
|
||||
|
||||
/**
|
||||
* 最大连接数, 小于1表示无限制
|
||||
*
|
||||
@@ -73,6 +81,13 @@ public @interface RestWebSocket {
|
||||
*/
|
||||
int wsmaxconns() default 0;
|
||||
|
||||
/**
|
||||
* 操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)
|
||||
*
|
||||
* @return 最大连接数
|
||||
*/
|
||||
int wsthreads() default 0;
|
||||
|
||||
/**
|
||||
* 最大消息体长度, 小于1表示无限制
|
||||
*
|
||||
|
||||
@@ -754,11 +754,12 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当Single模式下用户重复登陆时回调函数, 默认处理逻辑:关闭之前的WebSocket连接
|
||||
* 当Single模式下用户重复登陆时回调函数,默认处理方式: 关闭旧连接
|
||||
*
|
||||
* @return Future 可以为null, 为null或者Future值为false表示关闭新连接, Future值为true表示关闭旧连接
|
||||
*/
|
||||
public void onSingleRepeatConnect() {
|
||||
this._engine.node.forceCloseWebSocket(getUserid());
|
||||
public CompletableFuture<Boolean> onSingleRepeatConnect() {
|
||||
return forceCloseWebSocket(getUserid()).thenApply((r) -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.stream.*;
|
||||
import org.redkale.convert.Convert;
|
||||
import org.redkale.net.Cryptor;
|
||||
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
|
||||
import static org.redkale.net.http.WebSocketServlet.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -69,11 +70,17 @@ public class WebSocketEngine {
|
||||
@Comment("最大连接数, 为0表示无限制")
|
||||
protected int wsmaxconns;
|
||||
|
||||
@Comment("操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)")
|
||||
protected int wsthreads;
|
||||
|
||||
@Comment("最大消息体长度, 小于1表示无限制")
|
||||
protected int wsmaxbody;
|
||||
|
||||
@Comment("加密解密器")
|
||||
protected Cryptor cryptor;
|
||||
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval,
|
||||
int wsmaxconns, int wsmaxbody, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
int wsmaxconns, int wsthreads, int wsmaxbody, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
this.engineid = engineid;
|
||||
this.single = single;
|
||||
this.context = context;
|
||||
@@ -81,7 +88,9 @@ public class WebSocketEngine {
|
||||
this.node = node;
|
||||
this.liveinterval = liveinterval;
|
||||
this.wsmaxconns = wsmaxconns;
|
||||
this.wsthreads = wsthreads;
|
||||
this.wsmaxbody = wsmaxbody;
|
||||
this.cryptor = cryptor;
|
||||
this.logger = logger;
|
||||
this.index = sequence.getAndIncrement();
|
||||
}
|
||||
@@ -92,6 +101,7 @@ public class WebSocketEngine {
|
||||
this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
|
||||
if (liveinterval <= 0) return;
|
||||
if (props != null) this.wsmaxconns = props.getIntValue(WEBPARAM__WSMAXCONNS, this.wsmaxconns);
|
||||
if (props != null) this.wsthreads = props.getIntValue(WEBPARAM__WSTHREADS, this.wsthreads);
|
||||
if (props != null) this.wsmaxbody = props.getIntValue(WEBPARAM__WSMAXBODY, this.wsmaxbody);
|
||||
if (scheduler != null) return;
|
||||
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
@@ -102,8 +112,12 @@ public class WebSocketEngine {
|
||||
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5;
|
||||
final int intervalms = liveinterval * 1000;
|
||||
scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
getLocalWebSockets().stream().filter(x -> (now - x.getLastReadTime()) > intervalms).forEach(x -> x.sendPing());
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "WebSocketEngine schedule(interval=" + liveinterval + "s) ping error", t);
|
||||
}
|
||||
}, delay, liveinterval, TimeUnit.SECONDS);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(wsmaxconns:" + wsmaxconns + ", delay:" + delay + "s, interval:" + liveinterval + "s) scheduler executor");
|
||||
}
|
||||
@@ -130,7 +144,7 @@ public class WebSocketEngine {
|
||||
}
|
||||
|
||||
@Comment("从WebSocketEngine删除指定WebSocket")
|
||||
void remove(WebSocket socket) {
|
||||
void removeThenClose(WebSocket socket) {
|
||||
Serializable userid = socket._userid;
|
||||
if (single) {
|
||||
currconns.decrementAndGet();
|
||||
@@ -213,7 +227,7 @@ public class WebSocketEngine {
|
||||
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
|
||||
: ((message == null || message instanceof CharSequence || message instanceof byte[])
|
||||
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
|
||||
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
|
||||
packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
|
||||
CompletableFuture<Integer> future = null;
|
||||
if (single) {
|
||||
for (WebSocket websocket : websockets.values()) {
|
||||
@@ -270,7 +284,7 @@ public class WebSocketEngine {
|
||||
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
|
||||
: ((message == null || message instanceof CharSequence || message instanceof byte[])
|
||||
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
|
||||
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
|
||||
packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
|
||||
CompletableFuture<Integer> future = null;
|
||||
if (single) {
|
||||
for (Serializable userid : userids) {
|
||||
|
||||
@@ -32,9 +32,6 @@ public abstract class WebSocketNode {
|
||||
@Comment("存储用户ID的key前缀")
|
||||
public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:";
|
||||
|
||||
@Comment("存储用户数的key")
|
||||
public static final String SOURCE_SNCP_USERCOUNT_KEY = "sncpws_usercount";
|
||||
|
||||
@Comment("存储当前SNCP节点列表的key")
|
||||
public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs";
|
||||
|
||||
@@ -60,17 +57,30 @@ public abstract class WebSocketNode {
|
||||
//当前节点的本地WebSocketEngine
|
||||
protected WebSocketEngine localEngine;
|
||||
|
||||
protected Semaphore semaphore;
|
||||
|
||||
public void init(AnyValue conf) {
|
||||
if (sncpNodeAddresses != null) sncpNodeAddresses.initValueType(InetSocketAddress.class);
|
||||
if (localEngine != null) {
|
||||
int wsthreads = localEngine.wsthreads;
|
||||
if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8;
|
||||
if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy(AnyValue conf) {
|
||||
}
|
||||
|
||||
@Local
|
||||
public final Semaphore getSemaphore() {
|
||||
return semaphore;
|
||||
}
|
||||
|
||||
@Local
|
||||
public final void postDestroy(AnyValue conf) {
|
||||
if (this.localEngine == null) return;
|
||||
//关掉所有本地本地WebSocket
|
||||
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()));
|
||||
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()).join());
|
||||
if (sncpNodeAddresses != null && localSncpAddress != null) {
|
||||
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, localSncpAddress);
|
||||
}
|
||||
@@ -135,7 +145,12 @@ public abstract class WebSocketNode {
|
||||
* @return 地址列表
|
||||
*/
|
||||
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
|
||||
if (this.sncpNodeAddresses != null) return this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
if (this.sncpNodeAddresses != null) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> result = this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore());
|
||||
return result;
|
||||
}
|
||||
List<InetSocketAddress> rs = new ArrayList<>();
|
||||
rs.add(this.localSncpAddress);
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
@@ -176,7 +191,10 @@ public abstract class WebSocketNode {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) {
|
||||
return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid));
|
||||
}
|
||||
return this.sncpNodeAddresses.existsAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Boolean> rs = this.sncpNodeAddresses.existsAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +207,10 @@ public abstract class WebSocketNode {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) {
|
||||
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
|
||||
}
|
||||
return this.sncpNodeAddresses.getLongAsync(SOURCE_SNCP_USERCOUNT_KEY, 0L).thenApply(v -> v.intValue());
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Integer> rs = this.sncpNodeAddresses.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX).thenApply(v -> v.size());
|
||||
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,6 +220,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) {
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
|
||||
@@ -208,7 +230,9 @@ public abstract class WebSocketNode {
|
||||
return localFuture;
|
||||
}
|
||||
//远程节点关闭
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
|
||||
@@ -230,6 +254,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return WebSocketEngine
|
||||
*/
|
||||
@Local
|
||||
public final WebSocketEngine getLocalWebSocketEngine() {
|
||||
return this.localEngine;
|
||||
}
|
||||
@@ -243,6 +268,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(Object message, final Stream<? extends Serializable> userids) {
|
||||
return sendMessage((Convert) null, message, true, userids);
|
||||
}
|
||||
@@ -256,6 +282,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
|
||||
return sendMessage((Convert) null, message, true, userids);
|
||||
}
|
||||
@@ -270,6 +297,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Stream<? extends Serializable> userids) {
|
||||
return sendMessage(convert, message, true, userids);
|
||||
}
|
||||
@@ -284,6 +312,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Serializable... userids) {
|
||||
return sendMessage(convert, message, true, userids);
|
||||
}
|
||||
@@ -298,6 +327,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
|
||||
return sendMessage((Convert) null, message, last, userids);
|
||||
}
|
||||
@@ -312,6 +342,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
|
||||
return sendMessage((Convert) null, message, last, userids);
|
||||
}
|
||||
@@ -327,6 +358,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Stream<? extends Serializable> userids) {
|
||||
Object[] array = userids.toArray();
|
||||
Serializable[] ss = new Serializable[array.length];
|
||||
@@ -347,6 +379,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) {
|
||||
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids));
|
||||
@@ -354,7 +387,7 @@ public abstract class WebSocketNode {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
|
||||
return this.localEngine.sendMessage(message, last, userids);
|
||||
}
|
||||
final Object remoteMessage = formatRemoteMessage(message0);
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (Serializable userid : userids) {
|
||||
future = future == null ? sendOneMessage(remoteMessage, last, userid)
|
||||
@@ -370,6 +403,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final Object message) {
|
||||
return broadcastMessage((Convert) null, message, true);
|
||||
}
|
||||
@@ -382,6 +416,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message) {
|
||||
return broadcastMessage(wsrange, (Convert) null, message, true);
|
||||
}
|
||||
@@ -394,6 +429,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message) {
|
||||
return broadcastMessage(convert, message, true);
|
||||
}
|
||||
@@ -407,6 +443,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) {
|
||||
return broadcastMessage(wsrange, convert, message, true);
|
||||
}
|
||||
@@ -419,6 +456,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
|
||||
return broadcastMessage((Convert) null, message, last);
|
||||
}
|
||||
@@ -432,6 +470,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) {
|
||||
return broadcastMessage(wsrange, (Convert) null, message, last);
|
||||
}
|
||||
@@ -445,6 +484,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message0, final boolean last) {
|
||||
return broadcastMessage((WebSocketRange) null, convert, message0, last);
|
||||
}
|
||||
@@ -459,6 +499,7 @@ public abstract class WebSocketNode {
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) {
|
||||
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last));
|
||||
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
|
||||
@@ -467,7 +508,9 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(wsrange, message, last);
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
|
||||
@@ -484,7 +527,9 @@ public abstract class WebSocketNode {
|
||||
|
||||
private CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
|
||||
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneMessage(msg, last, userid));
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket want send message {userid:" + userid + ", content:'" + JsonConvert.root().convertTo(message) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket want send message {userid:" + userid + ", content:'" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : JsonConvert.root().convertTo(message)) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
}
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = localEngine.sendMessage(message, last, userid);
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
@@ -494,7 +539,9 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
//远程节点发送消息
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (addrs == null || addrs.isEmpty()) {
|
||||
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
|
||||
@@ -520,4 +567,17 @@ public abstract class WebSocketNode {
|
||||
if (sendConvert instanceof BinaryConvert) ((BinaryConvert) sendConvert).convertTo(message);
|
||||
return JsonConvert.root().convertTo(message);
|
||||
}
|
||||
|
||||
protected boolean tryAcquireSemaphore() {
|
||||
if (this.semaphore == null) return true;
|
||||
try {
|
||||
return this.semaphore.tryAcquire(6, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void releaseSemaphore() {
|
||||
if (this.semaphore != null) this.semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ package org.redkale.net.http;
|
||||
import org.redkale.util.Utility;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.net.Cryptor;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -23,8 +24,6 @@ import org.redkale.convert.*;
|
||||
*/
|
||||
public final class WebSocketPacket {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
static final WebSocketPacket NONE = new WebSocketPacket();
|
||||
|
||||
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
|
||||
@@ -201,6 +200,12 @@ public final class WebSocketPacket {
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
public String toSimpleString() {
|
||||
if (payload != null) return payload;
|
||||
if (bytes != null) return "byte[" + bytes.length + "]";
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : (receiveLength > 0 ? (", receivemsg=" + (receiveMessage instanceof byte[] ? ("byte[" + ((byte[]) receiveMessage).length + "]") : receiveMessage)) : "")) + (sendJson != null ? (", json=" + (sendMapconvable ? Utility.ofMap((Object[]) sendJson) : sendJson)) : "") + "]";
|
||||
@@ -210,10 +215,11 @@ public final class WebSocketPacket {
|
||||
* 消息编码
|
||||
*
|
||||
* @param supplier Supplier
|
||||
* @param cryptor Cryptor
|
||||
*
|
||||
* @return ByteBuffer[]
|
||||
*/
|
||||
ByteBuffer[] encode(final Supplier<ByteBuffer> supplier) {
|
||||
ByteBuffer[] encode(final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer, final Cryptor cryptor) {
|
||||
final byte opcode = (byte) (this.type.getValue() | 0x80);
|
||||
if (this.sendConvert != null) {
|
||||
Supplier<ByteBuffer> newsupplier = new Supplier<ByteBuffer>() {
|
||||
@@ -232,6 +238,7 @@ public final class WebSocketPacket {
|
||||
}
|
||||
};
|
||||
ByteBuffer[] buffers = this.sendMapconvable ? this.sendConvert.convertMapTo(newsupplier, (Object[]) sendJson) : this.sendConvert.convertTo(newsupplier, sendJson);
|
||||
if (cryptor != null) buffers = cryptor.encrypt(buffers, supplier, consumer);
|
||||
int len = 0;
|
||||
for (ByteBuffer buf : buffers) {
|
||||
len += buf.remaining();
|
||||
@@ -256,7 +263,27 @@ public final class WebSocketPacket {
|
||||
}
|
||||
|
||||
ByteBuffer buffer = supplier.get(); //确保ByteBuffer的capacity不能小于128
|
||||
final byte[] content = content();
|
||||
byte[] content = content();
|
||||
if (cryptor != null) {
|
||||
ByteBuffer[] ss = new ByteBuffer[]{ByteBuffer.wrap(content)};
|
||||
ByteBuffer[] bs = cryptor.encrypt(ss, supplier, consumer);
|
||||
if (bs != ss) {
|
||||
int r = 0;
|
||||
for (ByteBuffer bb : bs) {
|
||||
r += bb.remaining();
|
||||
}
|
||||
content = new byte[r];
|
||||
int index = 0;
|
||||
for (ByteBuffer bb : bs) {
|
||||
int re = bb.remaining();
|
||||
bb.get(content, index, re);
|
||||
index += re;
|
||||
}
|
||||
for (ByteBuffer bb : bs) {
|
||||
consumer.accept(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
final int len = content.length;
|
||||
if (len <= 0x7D) { //125
|
||||
buffer.put(opcode);
|
||||
@@ -444,10 +471,14 @@ public final class WebSocketPacket {
|
||||
}
|
||||
|
||||
void parseReceiveMessage(WebSocket webSocket, ByteBuffer... buffers) {
|
||||
if (webSocket._engine.cryptor != null) {
|
||||
HttpContext context = webSocket._engine.context;
|
||||
buffers = webSocket._engine.cryptor.decrypt(buffers, context.getBufferSupplier(), context.getBufferConsumer());
|
||||
}
|
||||
if (this.type == FrameType.TEXT) {
|
||||
Convert textConvert = webSocket.getTextConvert();
|
||||
if (textConvert == null) {
|
||||
this.receiveMessage = new String(this.getReceiveBytes(buffers), UTF_8);
|
||||
this.receiveMessage = new String(this.getReceiveBytes(buffers), StandardCharsets.UTF_8);
|
||||
this.receiveType = MessageType.STRING;
|
||||
} else {
|
||||
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.redkale.net.http;
|
||||
|
||||
import org.redkale.net.AsyncConnection;
|
||||
import org.redkale.net.Context;
|
||||
import static org.redkale.net.http.WebSocket.*;
|
||||
import org.redkale.net.http.WebSocketPacket.FrameType;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -34,7 +33,7 @@ class WebSocketRunner implements Runnable {
|
||||
|
||||
private final WebSocket webSocket;
|
||||
|
||||
protected final Context context;
|
||||
protected final HttpContext context;
|
||||
|
||||
private ByteBuffer readBuffer;
|
||||
|
||||
@@ -42,7 +41,7 @@ class WebSocketRunner implements Runnable {
|
||||
|
||||
private final AtomicBoolean writing = new AtomicBoolean();
|
||||
|
||||
private final BlockingQueue<QueueEntry> queue = new ArrayBlockingQueue(1024);
|
||||
private final BlockingQueue<QueueEntry> writeQueue = new ArrayBlockingQueue(512);
|
||||
|
||||
private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
|
||||
|
||||
@@ -50,7 +49,7 @@ class WebSocketRunner implements Runnable {
|
||||
|
||||
protected long lastReadTime;
|
||||
|
||||
WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel) {
|
||||
WebSocketRunner(HttpContext context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel) {
|
||||
this.context = context;
|
||||
this.engine = webSocket._engine;
|
||||
this.webSocket = webSocket;
|
||||
@@ -64,7 +63,7 @@ class WebSocketRunner implements Runnable {
|
||||
final boolean debug = context.getLogger().isLoggable(Level.FINEST);
|
||||
try {
|
||||
webSocket.onConnected();
|
||||
channel.setReadTimeoutSecond(300); //读取超时5分钟
|
||||
channel.setReadTimeoutSeconds(300); //读取超时5分钟
|
||||
if (channel.isOpen()) {
|
||||
final int wsmaxbody = webSocket._engine.wsmaxbody;
|
||||
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
|
||||
@@ -80,17 +79,19 @@ class WebSocketRunner implements Runnable {
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment1) {
|
||||
if (count < 1) {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner(userid="+webSocket.getUserid()+") abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
closeRunner(0, "read buffer count is " + count);
|
||||
return;
|
||||
}
|
||||
if (readBuffer == null) return;
|
||||
try {
|
||||
ByteBuffer readBuf = readBuffer;
|
||||
if (readBuf == null) return; //关闭后readBuffer为null
|
||||
lastReadTime = System.currentTimeMillis();
|
||||
readBuffer.flip();
|
||||
readBuf.flip();
|
||||
|
||||
WebSocketPacket onePacket = null;
|
||||
if (unfinishPacket != null) {
|
||||
if (unfinishPacket.receiveBody(webSocket, readBuffer)) { //已经接收完毕
|
||||
if (unfinishPacket.receiveBody(webSocket, readBuf)) { //已经接收完毕
|
||||
onePacket = unfinishPacket;
|
||||
unfinishPacket = null;
|
||||
for (ByteBuffer b : exBuffers) {
|
||||
@@ -98,8 +99,9 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
exBuffers.clear();
|
||||
} else { //需要继续接收
|
||||
readBuffer = context.pollBuffer();
|
||||
channel.read(readBuffer, null, this);
|
||||
readBuf = context.pollBuffer();
|
||||
readBuffer = readBuf;
|
||||
channel.read(readBuf, null, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -108,30 +110,31 @@ class WebSocketRunner implements Runnable {
|
||||
if (onePacket != null) packets.add(onePacket);
|
||||
try {
|
||||
while (true) {
|
||||
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuffer);
|
||||
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuf);
|
||||
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节
|
||||
if (packet != null && !packet.isReceiveFinished()) {
|
||||
unfinishPacket = packet;
|
||||
if (readBuffer.hasRemaining()) {
|
||||
exBuffers.add(readBuffer);
|
||||
readBuffer = context.pollBuffer();
|
||||
if (readBuf.hasRemaining()) {
|
||||
exBuffers.add(readBuf);
|
||||
readBuf = context.pollBuffer();
|
||||
readBuffer = readBuf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
packets.add(packet);
|
||||
if (packet == null || !readBuffer.hasRemaining()) break;
|
||||
if (packet == null || !readBuf.hasRemaining()) break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e);
|
||||
webSocket.onOccurException(e, null);
|
||||
}
|
||||
//继续监听消息
|
||||
readBuffer.clear();
|
||||
readBuf.clear();
|
||||
if (halfBytes.getValue() != null) {
|
||||
readBuffer.put(halfBytes.getValue());
|
||||
readBuf.put(halfBytes.getValue());
|
||||
halfBytes.setValue(null);
|
||||
}
|
||||
channel.read(readBuffer, null, this);
|
||||
channel.read(readBuf, null, this);
|
||||
|
||||
//消息处理
|
||||
for (final WebSocketPacket packet : packets) {
|
||||
@@ -192,6 +195,10 @@ class WebSocketRunner implements Runnable {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.WARNING, "WebSocketRunner(userid=" + webSocket.getUserid() + ") onMessage by received error", e);
|
||||
closeRunner(0, "websocket-received error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -220,15 +227,16 @@ class WebSocketRunner implements Runnable {
|
||||
boolean debug = context.getLogger().isLoggable(Level.FINEST);
|
||||
//System.out.println("推送消息");
|
||||
final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
|
||||
try {
|
||||
synchronized (writing) {
|
||||
if (writing.getAndSet(true)) {
|
||||
queue.add(new QueueEntry(futureResult, packet));
|
||||
writeQueue.add(new QueueEntry(futureResult, packet));
|
||||
return futureResult;
|
||||
}
|
||||
}
|
||||
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(this.context.getBufferSupplier());
|
||||
if (debug) context.getLogger().log(Level.FINEST, "sending websocket message: " + packet);
|
||||
try {
|
||||
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(this.context.getBufferSupplier(), this.context.getBufferConsumer(), webSocket._engine.cryptor);
|
||||
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet);
|
||||
|
||||
this.lastSendTime = System.currentTimeMillis();
|
||||
channel.write(buffers, buffers, new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||
|
||||
@@ -256,7 +264,7 @@ class WebSocketRunner implements Runnable {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
if (index >= 0) { //ByteBuffer[]统一回收的可以采用此写法
|
||||
channel.write(attachments, index, attachments.length - index, attachments, this);
|
||||
return;
|
||||
}
|
||||
@@ -271,37 +279,40 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
QueueEntry entry = null;
|
||||
synchronized (writing) {
|
||||
entry = queue.poll();
|
||||
entry = writeQueue.poll();
|
||||
if (entry == null) writing.set(false);
|
||||
}
|
||||
if (entry != null) {
|
||||
future = entry.future;
|
||||
ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier());
|
||||
ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), webSocket._engine.cryptor);
|
||||
lastSendTime = System.currentTimeMillis();
|
||||
if (debug) context.getLogger().log(Level.FINEST, "sending websocket message: " + entry.packet);
|
||||
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + entry.packet);
|
||||
channel.write(buffers, buffers, this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.WARNING, "WebSocket sendMessage abort on rewrite, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e);
|
||||
future.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on rewrite");
|
||||
context.getLogger().log(Level.WARNING, "WebSocket sendMessage abort on rewrite, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachments) {
|
||||
writing.set(false);
|
||||
future.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler");
|
||||
if (exc != null) {
|
||||
context.getLogger().log(Level.FINE, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc);
|
||||
}
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler");
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Exception t) {
|
||||
writing.set(false);
|
||||
context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on channel.write");
|
||||
futureResult.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on channel.write");
|
||||
context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
|
||||
|
||||
}
|
||||
return futureResult;
|
||||
}
|
||||
@@ -315,14 +326,16 @@ class WebSocketRunner implements Runnable {
|
||||
synchronized (this) {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
channel.dispose();
|
||||
context.offerBuffer(readBuffer);
|
||||
readBuffer = null;
|
||||
engine.remove(webSocket);
|
||||
engine.removeThenClose(webSocket);
|
||||
webSocket.onClose(code, reason);
|
||||
QueueEntry entry = writeQueue.poll();
|
||||
while (entry != null) {
|
||||
entry.future.complete(RETCODE_WSOCKET_CLOSED);
|
||||
entry = writeQueue.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ import java.nio.channels.CompletionHandler;
|
||||
import java.security.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
import org.redkale.convert.Convert;
|
||||
import org.redkale.net.Cryptor;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -49,9 +50,15 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
@Comment("WebScoket服务器最大连接数,为0表示无限制")
|
||||
public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns";
|
||||
|
||||
@Comment("WebScoket服务器操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)")
|
||||
public static final String WEBPARAM__WSTHREADS = "wsthreads";
|
||||
|
||||
@Comment("最大消息体长度, 小于1表示无限制")
|
||||
public static final String WEBPARAM__WSMAXBODY = "wsmaxbody";
|
||||
|
||||
@Comment("加密解密器")
|
||||
public static final String WEBPARAM__CRYPTOR = "cryptor";
|
||||
|
||||
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
|
||||
public static final int DEFAILT_LIVEINTERVAL = 15;
|
||||
|
||||
@@ -72,12 +79,18 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
//同RestWebSocket.wsmaxconns
|
||||
protected int wsmaxconns = 0;
|
||||
|
||||
//同RestWebSocket.wsthreads
|
||||
protected int wsthreads = 0;
|
||||
|
||||
//同RestWebSocket.wsmaxbody
|
||||
protected int wsmaxbody = 32 * 1024;
|
||||
|
||||
//同RestWebSocket.anyuser
|
||||
protected boolean anyuser = false;
|
||||
|
||||
//同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到
|
||||
protected Cryptor cryptor;
|
||||
|
||||
@Resource(name = "jsonconvert")
|
||||
protected Convert jsonConvert;
|
||||
|
||||
@@ -124,12 +137,26 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
|
||||
}
|
||||
if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert;
|
||||
|
||||
{
|
||||
AnyValue props = conf;
|
||||
if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties");
|
||||
if (props != null) {
|
||||
String cryptorClass = props.getValue(WEBPARAM__CRYPTOR);
|
||||
if (cryptorClass != null && !cryptorClass.isEmpty()) {
|
||||
try {
|
||||
this.cryptor = (Cryptor) Thread.currentThread().getContextClassLoader().loadClass(cryptorClass).getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//存在WebSocketServlet,则此WebSocketNode必须是本地模式Service
|
||||
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
|
||||
this.single, context, liveinterval, wsmaxconns, wsmaxbody, this.node, this.sendConvert, logger);
|
||||
this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, this.cryptor, this.node, this.sendConvert, logger);
|
||||
this.node.init(conf);
|
||||
this.node.localEngine.init(conf);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -220,12 +247,37 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
webSocket._userid = userid;
|
||||
if (single && !anyuser) {
|
||||
WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, nex) -> {
|
||||
if (rs) webSocket.onSingleRepeatConnect();
|
||||
if (rs) {
|
||||
CompletableFuture<Boolean> rcFuture = webSocket.onSingleRepeatConnect();
|
||||
Consumer<Boolean> task = (oldkilled) -> {
|
||||
if (oldkilled) {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
response.finish(true);
|
||||
} else { //关闭新连接
|
||||
response.finish(true);
|
||||
}
|
||||
};
|
||||
if (rcFuture == null) {
|
||||
task.accept(false);
|
||||
} else {
|
||||
rcFuture.whenComplete((r, e) -> {
|
||||
if (e != null) {
|
||||
response.finish(true);
|
||||
} else {
|
||||
task.accept(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
response.finish(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
|
||||
@@ -280,6 +280,7 @@ public abstract class Sncp {
|
||||
final List<Method> methods = SncpClient.parseMethod(serviceImplClass);
|
||||
final String supDynName = serviceImplClass.getName().replace('.', '/');
|
||||
final String clientName = SncpClient.class.getName().replace('.', '/');
|
||||
final String resDesc = Type.getDescriptor(Resource.class);
|
||||
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
||||
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
||||
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
||||
@@ -305,7 +306,7 @@ public abstract class Sncp {
|
||||
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0 = cw.visitAnnotation(resDesc, true);
|
||||
av0.visit("name", name);
|
||||
av0.visitEnd();
|
||||
}
|
||||
@@ -754,7 +755,7 @@ public abstract class Sncp {
|
||||
final AnyValue conf) {
|
||||
try {
|
||||
final Class newClazz = createLocalServiceClass(classLoader, name, serviceImplClass);
|
||||
T rs = (T) newClazz.newInstance();
|
||||
T rs = (T) newClazz.getDeclaredConstructor().newInstance();
|
||||
//--------------------------------------
|
||||
Service remoteService = null;
|
||||
{
|
||||
@@ -881,6 +882,7 @@ public abstract class Sncp {
|
||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
||||
final String supDynName = serviceTypeOrImplClass.getName().replace('.', '/');
|
||||
final String clientName = SncpClient.class.getName().replace('.', '/');
|
||||
final String resDesc = Type.getDescriptor(Resource.class);
|
||||
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
||||
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
||||
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
||||
@@ -888,7 +890,7 @@ public abstract class Sncp {
|
||||
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName();
|
||||
try {
|
||||
Class newClazz = loader.loadClass(newDynName.replace('/', '.'));
|
||||
T rs = (T) newClazz.newInstance();
|
||||
T rs = (T) newClazz.getDeclaredConstructor().newInstance();
|
||||
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
|
||||
client.setRemoteGroups(groups);
|
||||
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
|
||||
@@ -907,7 +909,7 @@ public abstract class Sncp {
|
||||
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, serviceTypeOrImplClass.isInterface() ? new String[]{supDynName} : null);
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0 = cw.visitAnnotation(resDesc, true);
|
||||
av0.visit("name", name);
|
||||
av0.visitEnd();
|
||||
}
|
||||
@@ -1069,7 +1071,7 @@ public abstract class Sncp {
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
T rs = (T) newClazz.newInstance();
|
||||
T rs = (T) newClazz.getDeclaredConstructor().newInstance();
|
||||
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
|
||||
client.setRemoteGroups(groups);
|
||||
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
|
||||
|
||||
@@ -102,6 +102,7 @@ public interface SncpAsyncHandler<V, A> extends CompletionHandler<V, A> {
|
||||
final boolean handlerinterface = handlerClass.isInterface();
|
||||
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||
final String sncpHandlerName = SncpAsyncHandler.class.getName().replace('.', '/');
|
||||
final String cpDesc = Type.getDescriptor(ConstructorParameters.class);
|
||||
final String sncpHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class);
|
||||
final String sncpFutureDesc = Type.getDescriptor(CompletableFuture.class);
|
||||
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + SncpAsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||
@@ -124,7 +125,7 @@ public interface SncpAsyncHandler<V, A> extends CompletionHandler<V, A> {
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + sncpHandlerDesc + ")V", null, null));
|
||||
//mv.setDebug(true);
|
||||
{
|
||||
av0 = mv.visitAnnotation("org/redkale/util/ConstructorParameters;", true);
|
||||
av0 = mv.visitAnnotation(cpDesc, true);
|
||||
{
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
av1.visit(null, "sncphandler");
|
||||
|
||||
@@ -296,7 +296,7 @@ public final class SncpClient {
|
||||
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null;
|
||||
final BsonReader reader = bsonConvert.pollBsonReader();
|
||||
CompletableFuture<byte[]> future = remote0(handlerFunc, remoteGroupTransport, null, action, params);
|
||||
if (action.boolReturnTypeFuture) {
|
||||
if (action.boolReturnTypeFuture) { //与handlerFuncIndex互斥
|
||||
CompletableFuture result = action.futureCreator.create();
|
||||
future.whenComplete((v, e) -> {
|
||||
try {
|
||||
@@ -355,7 +355,12 @@ public final class SncpClient {
|
||||
CompletableFuture<AsyncConnection> connFuture = transport.pollConnection(addr);
|
||||
return connFuture.thenCompose(conn0 -> {
|
||||
final CompletableFuture<byte[]> future = new CompletableFuture();
|
||||
if (conn0 == null || !conn0.isOpen()) {
|
||||
if (conn0 == null) {
|
||||
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
|
||||
return future;
|
||||
}
|
||||
if (!conn0.isOpen()) {
|
||||
conn0.dispose();
|
||||
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
|
||||
return future;
|
||||
}
|
||||
@@ -377,7 +382,7 @@ public final class SncpClient {
|
||||
transport.offerBuffer(attachments[i]);
|
||||
}
|
||||
}
|
||||
if (index == 0) {
|
||||
if (index == 0) { //ByteBuffer[]不统一回收的必须采用此写法分开
|
||||
conn.write(attachments, attachments, this);
|
||||
return;
|
||||
} else if (index > 0) {
|
||||
@@ -396,6 +401,7 @@ public final class SncpClient {
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment2) {
|
||||
try {
|
||||
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||
transport.offerBuffer(buffer);
|
||||
@@ -439,8 +445,18 @@ public final class SncpClient {
|
||||
buffer.get(body, 0, respBodyLength);
|
||||
success();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote response error"));
|
||||
transport.offerConnection(true, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
handler.failed(e, handlerAttach);
|
||||
}
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void success() {
|
||||
future.complete(this.body);
|
||||
transport.offerBuffer(buffer);
|
||||
@@ -467,7 +483,6 @@ public final class SncpClient {
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
@@ -475,15 +490,21 @@ public final class SncpClient {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
handler.failed(exc, handlerAttach);
|
||||
}
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachment) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
handler.failed(exc, handlerAttach);
|
||||
}
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
@@ -547,6 +568,7 @@ public final class SncpClient {
|
||||
|
||||
protected final Creator<? extends CompletableFuture> futureCreator;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SncpAction(final Class clazz, Method method, DLong actionid) {
|
||||
this.actionid = actionid == null ? Sncp.hash(method) : actionid;
|
||||
Type rt = TypeToken.getGenericType(method.getGenericReturnType(), clazz);
|
||||
@@ -595,7 +617,7 @@ public final class SncpClient {
|
||||
for (Annotation ann : anns[i]) {
|
||||
if (ann.annotationType() == RpcCall.class) {
|
||||
try {
|
||||
atts[i + 1] = ((RpcCall) ann).value().newInstance();
|
||||
atts[i + 1] = ((RpcCall) ann).value().getDeclaredConstructor().newInstance();
|
||||
hasattr = true;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e);
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
*/
|
||||
package org.redkale.net.sncp;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.util.function.*;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -22,11 +17,26 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public class SncpContext extends Context {
|
||||
|
||||
public SncpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext,
|
||||
int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
|
||||
int maxbody, Charset charset, InetSocketAddress address, ResourceFactory resourceFactory,
|
||||
PrepareServlet prepare, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
super(serverStartTime, logger, executor, sslContext, bufferCapacity, bufferPool, responsePool,
|
||||
maxbody, charset, address, resourceFactory, prepare, readTimeoutSecond, writeTimeoutSecond);
|
||||
public SncpContext(SncpContextConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Consumer<ByteBuffer> getBufferConsumer() {
|
||||
return super.getBufferConsumer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer buffer) {
|
||||
super.offerBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
super.offerBuffer(buffers);
|
||||
}
|
||||
|
||||
public static class SncpContextConfig extends ContextConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void execute(SncpRequest request, SncpResponse response) throws IOException {
|
||||
if (bufferSupplier == null) {
|
||||
bufferSupplier = request.getContext().getBufferSupplier();
|
||||
@@ -148,7 +149,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
Object[] sncpParams = handler.sncp_getParams();
|
||||
future.whenComplete((v, e) -> {
|
||||
if (e != null) {
|
||||
response.getContext().getLogger().log(Level.INFO, "sncp CompleteAsync error(" + request + ")", e);
|
||||
response.getContext().getLogger().log(Level.SEVERE, "sncp CompleteAsync error(" + request + ")", e);
|
||||
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
|
||||
return;
|
||||
}
|
||||
@@ -161,7 +162,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", t);
|
||||
response.getContext().getLogger().log(Level.SEVERE, "sncp execute error(" + request + ")", t);
|
||||
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
|
||||
}
|
||||
}
|
||||
@@ -586,7 +587,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
SncpServletAction instance = (SncpServletAction) newClazz.newInstance();
|
||||
SncpServletAction instance = (SncpServletAction) newClazz.getDeclaredConstructor().newInstance();
|
||||
instance.method = method;
|
||||
java.lang.reflect.Type[] ptypes = TypeToken.getGenericType(method.getGenericParameterTypes(), serviceClass);
|
||||
java.lang.reflect.Type[] types = new java.lang.reflect.Type[ptypes.length + 1];
|
||||
@@ -606,7 +607,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
for (Annotation ann : anns[i]) {
|
||||
if (ann.annotationType() == RpcCall.class) {
|
||||
try {
|
||||
atts[i + 1] = ((RpcCall) ann).value().newInstance();
|
||||
atts[i + 1] = ((RpcCall) ann).value().getDeclaredConstructor().newInstance();
|
||||
hasattr = true;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e);
|
||||
|
||||
@@ -52,6 +52,11 @@ public final class SncpResponse extends Response<SncpContext, SncpRequest> {
|
||||
if (this.addrBytes.length != 4) throw new RuntimeException("SNCP serverAddress only support IPv4");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
super.offerBuffer(buffers);
|
||||
}
|
||||
|
||||
public void finish(final int retcode, final BsonWriter out) {
|
||||
if (out == null) {
|
||||
final ByteBuffer buffer = pollWriteReadBuffer();
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import org.redkale.convert.bson.*;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.sncp.SncpContext.SncpContextConfig;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -106,8 +107,26 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
|
||||
AtomicLong createResponseCounter = new AtomicLong();
|
||||
AtomicLong cycleResponseCounter = new AtomicLong();
|
||||
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
||||
SncpContext sncpcontext = new SncpContext(this.serverStartTime, this.logger, executor, this.sslContext, rcapacity, bufferPool, responsePool,
|
||||
this.maxbody, this.charset, this.address, this.resourceFactory, this.prepare, this.readTimeoutSecond, this.writeTimeoutSecond);
|
||||
|
||||
final SncpContextConfig contextConfig = new SncpContextConfig();
|
||||
contextConfig.serverStartTime = this.serverStartTime;
|
||||
contextConfig.logger = this.logger;
|
||||
contextConfig.executor = this.executor;
|
||||
contextConfig.sslContext = this.sslContext;
|
||||
contextConfig.bufferCapacity = rcapacity;
|
||||
contextConfig.bufferPool = bufferPool;
|
||||
contextConfig.responsePool = responsePool;
|
||||
contextConfig.maxconns = this.maxconns;
|
||||
contextConfig.maxbody = this.maxbody;
|
||||
contextConfig.charset = this.charset;
|
||||
contextConfig.address = this.address;
|
||||
contextConfig.prepare = this.prepare;
|
||||
contextConfig.resourceFactory = this.resourceFactory;
|
||||
contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds;
|
||||
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
|
||||
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
|
||||
|
||||
SncpContext sncpcontext = new SncpContext(contextConfig);
|
||||
responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext)));
|
||||
return sncpcontext;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
package org.redkale.service;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.reflect.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -23,61 +24,28 @@ import java.util.*;
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(value = {ElementType.FIELD})
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target({FIELD})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(RetLabel.RetLabels.class)
|
||||
public @interface RetLabel {
|
||||
|
||||
String value();
|
||||
|
||||
public static class RetCode {
|
||||
String locale() default "";
|
||||
|
||||
protected final Map<Integer, String> rets = new HashMap();
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({FIELD})
|
||||
@Retention(RUNTIME)
|
||||
@interface RetLabels {
|
||||
|
||||
protected final Class clazz;
|
||||
|
||||
protected boolean inited;
|
||||
|
||||
public RetCode(Class clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public RetResult retResult(int retcode) {
|
||||
if (retcode == 0) return RetResult.success();
|
||||
return new RetResult(retcode, retInfo(retcode));
|
||||
}
|
||||
|
||||
public RetResult retResult(int retcode, Object... args) {
|
||||
if (retcode == 0) return RetResult.success();
|
||||
if (args == null || args.length < 1) return new RetResult(retcode, retInfo(retcode));
|
||||
String info = MessageFormat.format(retInfo(retcode), args);
|
||||
return new RetResult(retcode, info);
|
||||
}
|
||||
|
||||
public RetResult set(RetResult result, int retcode, Object... args) {
|
||||
if (retcode == 0) return result.retcode(0).retinfo("");
|
||||
if (args == null || args.length < 1) return result.retcode(retcode).retinfo(retInfo(retcode));
|
||||
String info = MessageFormat.format(retInfo(retcode), args);
|
||||
return result.retcode(retcode).retinfo(info);
|
||||
}
|
||||
|
||||
public String retInfo(int retcode) {
|
||||
if (retcode == 0) return "成功";
|
||||
if (!this.inited) {
|
||||
synchronized (this) {
|
||||
if (!this.inited) {
|
||||
rets.putAll(RetLoader.load(clazz));
|
||||
}
|
||||
this.inited = true;
|
||||
}
|
||||
}
|
||||
return rets.getOrDefault(retcode, "未知错误");
|
||||
}
|
||||
RetLabel[] value();
|
||||
}
|
||||
|
||||
public static abstract class RetLoader {
|
||||
|
||||
public static Map<Integer, String> load(Class clazz) {
|
||||
final Map<Integer, String> rets = new HashMap<>();
|
||||
public static Map<String, Map<Integer, String>> loadMap(Class clazz) {
|
||||
final Map<String, Map<Integer, String>> rets = new HashMap<>();
|
||||
for (Field field : clazz.getFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (field.getType() != int.class) continue;
|
||||
@@ -90,9 +58,14 @@ public @interface RetLabel {
|
||||
ex.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
rets.put(value, info.value());
|
||||
rets.computeIfAbsent(info.locale(), (k) -> new HashMap<>()).put(value, info.value());
|
||||
}
|
||||
return rets;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Map<Integer, String> load(Class clazz) {
|
||||
return loadMap(clazz).computeIfAbsent("", (k) -> new HashMap<>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
@@ -57,6 +58,10 @@ public class RetResult<T> {
|
||||
return new RetResult();
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<RetResult<T>> successFuture() {
|
||||
return CompletableFuture.completedFuture(new RetResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断结果是否成功返回, retcode = 0 视为成功, 否则视为错误码
|
||||
*
|
||||
|
||||
@@ -9,8 +9,9 @@ import static org.redkale.net.http.WebSocket.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.net.WorkThread;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -39,11 +40,19 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
public CompletableFuture<List<String>> getWebSocketAddresses(final @RpcTargetAddress InetSocketAddress targetAddress, final Serializable groupid) {
|
||||
if (localSncpAddress == null || !localSncpAddress.equals(targetAddress)) return remoteWebSocketAddresses(targetAddress, groupid);
|
||||
if (this.localEngine == null) return CompletableFuture.completedFuture(new ArrayList<>());
|
||||
|
||||
ExecutorService executor = null;
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread instanceof WorkThread) {
|
||||
executor = ((WorkThread) thread).getExecutor();
|
||||
}
|
||||
if (executor == null) executor = ForkJoinPool.commonPool();
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
final List<String> rs = new ArrayList<>();
|
||||
this.localEngine.getLocalWebSockets(groupid).forEach(x -> rs.add(x.getRemoteAddr()));
|
||||
return rs;
|
||||
});
|
||||
}, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,9 +77,10 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.incr(SOURCE_SNCP_USERCOUNT_KEY));
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_ADDRS_KEY, sncpAddr));
|
||||
if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore());
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
|
||||
return future;
|
||||
}
|
||||
@@ -85,8 +95,9 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.decr(SOURCE_SNCP_USERCOUNT_KEY));
|
||||
if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore());
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
|
||||
return future;
|
||||
}
|
||||
@@ -102,8 +113,10 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> changeUserid(Serializable olduserid, Serializable newuserid, InetSocketAddress sncpAddr) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + olduserid, sncpAddr));
|
||||
if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore());
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + sncpAddr);
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.convert.json.*;
|
||||
@@ -27,6 +27,7 @@ import org.redkale.util.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Local
|
||||
@AutoLoad(false)
|
||||
@ResourceType(CacheSource.class)
|
||||
@@ -62,6 +63,10 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
|
||||
protected final ConcurrentHashMap<String, CacheEntry<Object>> container = new ConcurrentHashMap<>();
|
||||
|
||||
protected final BiConsumer futureCompleteConsumer = (r, t) -> {
|
||||
if (t != null) logger.log(Level.SEVERE, "CompletableFuture complete error", (Throwable) t);
|
||||
};
|
||||
|
||||
@RpcRemote
|
||||
protected CacheSource<V> remoteSource;
|
||||
|
||||
@@ -85,6 +90,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void init(AnyValue conf) {
|
||||
if (this.convert == null) this.convert = this.defaultConvert;
|
||||
if (this.convert == null) this.convert = JsonConvert.root();
|
||||
@@ -104,7 +110,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
|
||||
if (expireHandlerClass != null) {
|
||||
try {
|
||||
this.expireHandler = (Consumer<CacheEntry>) Thread.currentThread().getContextClassLoader().loadClass(expireHandlerClass).newInstance();
|
||||
this.expireHandler = (Consumer<CacheEntry>) Thread.currentThread().getContextClassLoader().loadClass(expireHandlerClass).getDeclaredConstructor().newInstance();
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e);
|
||||
}
|
||||
@@ -117,6 +123,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
});
|
||||
final List<String> keys = new ArrayList<>();
|
||||
scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
keys.clear();
|
||||
int now = (int) (System.currentTimeMillis() / 1000);
|
||||
container.forEach((k, x) -> {
|
||||
@@ -128,6 +135,9 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
CacheEntry entry = container.remove(key);
|
||||
if (expireHandler != null && entry != null) expireHandler.accept(entry);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "CacheMemorySource schedule(interval=" + 10 + "s) error", t);
|
||||
}
|
||||
}, 10, 10, TimeUnit.SECONDS);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
|
||||
}
|
||||
@@ -320,6 +330,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V get(String key) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
@@ -362,6 +373,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
@SuppressWarnings("unchecked")
|
||||
public V getAndRefresh(String key, final int expireSeconds) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
@@ -375,6 +387,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getStringAndRefresh(String key, final int expireSeconds) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
@@ -427,7 +440,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
protected void set(CacheEntryType cacheType, String key, Object value) {
|
||||
@@ -464,19 +477,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setAsync(String key, V value) {
|
||||
return CompletableFuture.runAsync(() -> set(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> set(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setStringAsync(String key, String value) {
|
||||
return CompletableFuture.runAsync(() -> setString(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> setString(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setLongAsync(String key, long value) {
|
||||
return CompletableFuture.runAsync(() -> setLong(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> setLong(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
protected void set(CacheEntryType cacheType, int expireSeconds, String key, Object value) {
|
||||
@@ -513,19 +526,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setAsync(int expireSeconds, String key, V value) {
|
||||
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setStringAsync(int expireSeconds, String key, String value) {
|
||||
return CompletableFuture.runAsync(() -> setString(expireSeconds, key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> setString(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setLongAsync(int expireSeconds, String key, long value) {
|
||||
return CompletableFuture.runAsync(() -> setLong(expireSeconds, key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> setLong(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -540,7 +553,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -559,7 +572,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> incrAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> incr(key), getExecutor());
|
||||
return CompletableFuture.supplyAsync(() -> incr(key), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -581,7 +594,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> incrAsync(final String key, long num) {
|
||||
return CompletableFuture.supplyAsync(() -> incr(key, num), getExecutor());
|
||||
return CompletableFuture.supplyAsync(() -> incr(key, num), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -593,7 +606,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> decrAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> decr(key), getExecutor());
|
||||
return CompletableFuture.supplyAsync(() -> decr(key), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -605,13 +618,13 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> decrAsync(final String key, long num) {
|
||||
return CompletableFuture.supplyAsync(() -> decr(key, num), getExecutor());
|
||||
return CompletableFuture.supplyAsync(() -> decr(key, num), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeAsync(final String key) {
|
||||
return CompletableFuture.runAsync(() -> remove(key), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> remove(key), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -759,19 +772,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendListItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> appendStringListItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> appendStringListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendLongListItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -804,19 +817,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeListItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> removeListItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> removeListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> removeStringListItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> removeStringListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeLongListItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> removeLongListItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> removeLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
protected void appendSetItem(CacheEntryType cacheType, String key, Object value) {
|
||||
@@ -854,19 +867,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> appendStringSetItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> appendStringSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendLongSetItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -899,19 +912,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> removeSetItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> removeSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> removeStringSetItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> removeStringSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeLongSetItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> removeLongSetItem(key, value), getExecutor());
|
||||
return CompletableFuture.runAsync(() -> removeLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -919,6 +932,22 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return new ArrayList<>(container.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryKeysStartsWith(String startsWith) {
|
||||
if (startsWith == null) return queryKeys();
|
||||
List<String> rs = new ArrayList<>();
|
||||
container.keySet().stream().filter(x -> x.startsWith(startsWith)).forEach(x -> rs.add(x));
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryKeysEndsWith(String endsWith) {
|
||||
if (endsWith == null) return queryKeys();
|
||||
List<String> rs = new ArrayList<>();
|
||||
container.keySet().stream().filter(x -> x.endsWith(endsWith)).forEach(x -> rs.add(x));
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getKeySize() {
|
||||
return container.size();
|
||||
@@ -939,6 +968,16 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.completedFuture(new ArrayList<>(container.keySet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<String>> queryKeysStartsWithAsync(String startsWith) {
|
||||
return CompletableFuture.completedFuture(queryKeysStartsWith(startsWith));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<String>> queryKeysEndsWithAsync(String endsWith) {
|
||||
return CompletableFuture.completedFuture(queryKeysEndsWith(endsWith));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> getKeySizeAsync() {
|
||||
return CompletableFuture.completedFuture(container.size());
|
||||
|
||||
@@ -14,6 +14,11 @@ import org.redkale.convert.json.JsonFactory;
|
||||
import org.redkale.util.ConstructorParameters;
|
||||
|
||||
/**
|
||||
* Redkale中缓存数据源的核心类。 主要供业务开发者使用, 技术开发者提供CacheSource的实现。<br>
|
||||
* CacheSource提供三种数据类型操作: String、Long和泛型指定的数据类型。<br>
|
||||
* String统一用setString、getString等系列方法。<br>
|
||||
* Long统一用setLong、getLong、incr等系列方法。<br>
|
||||
* 其他则供自定义数据类型使用。
|
||||
*
|
||||
* @param <V> value的类型
|
||||
* <p>
|
||||
@@ -93,6 +98,10 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public List<String> queryKeys();
|
||||
|
||||
public List<String> queryKeysStartsWith(String startsWith);
|
||||
|
||||
public List<String> queryKeysEndsWith(String endsWith);
|
||||
|
||||
public int getKeySize();
|
||||
|
||||
public List<CacheEntry<Object>> queryList();
|
||||
@@ -210,6 +219,10 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public CompletableFuture<List<String>> queryKeysAsync();
|
||||
|
||||
public CompletableFuture<List<String>> queryKeysStartsWithAsync(String startsWith);
|
||||
|
||||
public CompletableFuture<List<String>> queryKeysEndsWithAsync(String endsWith);
|
||||
|
||||
public CompletableFuture<Integer> getKeySizeAsync();
|
||||
|
||||
public CompletableFuture<List<CacheEntry< Object>>> queryListAsync();
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.redkale.util.*;
|
||||
* @param <T> Entity类的类型
|
||||
* @param <F> 字段的类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class DataCallArrayAttribute<T, F> implements Attribute<T[], F> {
|
||||
|
||||
public static final DataCallArrayAttribute instance = new DataCallArrayAttribute();
|
||||
|
||||
2469
src/org/redkale/source/DataJdbcOldSource.java
Normal file
2469
src/org/redkale/source/DataJdbcOldSource.java
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -33,26 +33,28 @@ public interface DataSource {
|
||||
|
||||
//----------------------insertAsync-----------------------------
|
||||
/**
|
||||
* 新增记录, 多对象必须是同一个Entity类 <br>
|
||||
* 新增记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param values Entity对象
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> void insert(final T... values);
|
||||
public <T> int insert(final T... values);
|
||||
|
||||
/**
|
||||
* 新增记录, 多对象必须是同一个Entity类 <br>
|
||||
* 新增记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param values Entity对象
|
||||
*
|
||||
* @return CompletableFuture
|
||||
*/
|
||||
public <T> CompletableFuture<Void> insertAsync(final T... values);
|
||||
public <T> CompletableFuture<Integer> insertAsync(final T... values);
|
||||
|
||||
//-------------------------deleteAsync--------------------------
|
||||
/**
|
||||
* 删除指定主键值的记录, 多对象必须是同一个Entity类 <br>
|
||||
* 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
@@ -63,7 +65,7 @@ public interface DataSource {
|
||||
public <T> int delete(final T... values);
|
||||
|
||||
/**
|
||||
* 删除指定主键值的记录, 多对象必须是同一个Entity类 <br>
|
||||
* 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
@@ -74,7 +76,7 @@ public interface DataSource {
|
||||
public <T> CompletableFuture<Integer> deleteAsync(final T... values);
|
||||
|
||||
/**
|
||||
* 删除指定主键值的记录 <br>
|
||||
* 删除指定主键值的记录,多主键值必须在同一张表中 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
@@ -86,7 +88,7 @@ public interface DataSource {
|
||||
public <T> int delete(final Class<T> clazz, final Serializable... ids);
|
||||
|
||||
/**
|
||||
* 删除指定主键值的记录 <br>
|
||||
* 删除指定主键值的记录,多主键值必须在同一张表中 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
@@ -151,7 +153,7 @@ public interface DataSource {
|
||||
|
||||
//------------------------updateAsync---------------------------
|
||||
/**
|
||||
* 更新记录, 多对象必须是同一个Entity类 <br>
|
||||
* 更新记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
|
||||
* 等价SQL: <br>
|
||||
* UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1} <br>
|
||||
* UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2} <br>
|
||||
@@ -165,7 +167,7 @@ public interface DataSource {
|
||||
public <T> int update(final T... values);
|
||||
|
||||
/**
|
||||
* 更新记录, 多对象必须是同一个Entity类 <br>
|
||||
* 更新记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
|
||||
* 等价SQL: <br>
|
||||
* UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1} <br>
|
||||
* UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2} <br>
|
||||
|
||||
@@ -25,6 +25,8 @@ public final class DataSources {
|
||||
|
||||
public static final String JDBC_CONNECTIONSMAX = "javax.persistence.connections.limit";
|
||||
|
||||
public static final String JDBC_CONNECTIONSCAPACITY = "javax.persistence.connections.bufcapacity";
|
||||
|
||||
public static final String JDBC_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate";
|
||||
|
||||
public static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.sqltemplate";
|
||||
@@ -33,6 +35,12 @@ public final class DataSources {
|
||||
|
||||
public static final String JDBC_TABLECOPY_SQLTEMPLATE = "javax.persistence.tablecopy.sqltemplate";
|
||||
|
||||
public static final String JDBC_CONNECTTIMEOUT_SECONDS = "javax.persistence.connecttimeout";
|
||||
|
||||
public static final String JDBC_READTIMEOUT_SECONDS = "javax.persistence.readtimeout";
|
||||
|
||||
public static final String JDBC_WRITETIMEOUT_SECONDS = "javax.persistence.writetimeout";
|
||||
|
||||
public static final String JDBC_URL = "javax.persistence.jdbc.url";
|
||||
|
||||
public static final String JDBC_USER = "javax.persistence.jdbc.user";
|
||||
@@ -47,11 +55,11 @@ public final class DataSources {
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, Properties prop) throws IOException {
|
||||
return new DataJdbcSource(unitName, prop, prop);
|
||||
return new DataJdbcSource(unitName, null, prop, prop);
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, Properties readprop, Properties writeprop) throws IOException {
|
||||
return new DataJdbcSource(unitName, readprop, writeprop);
|
||||
return new DataJdbcSource(unitName, null, readprop, writeprop);
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName) throws IOException {
|
||||
@@ -60,9 +68,9 @@ public final class DataSources {
|
||||
: new File(System.getProperty(DATASOURCE_CONFPATH)).toURI().toURL());
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, URL url) throws IOException {
|
||||
if (url == null) url = DataSources.class.getResource("/persistence.xml");
|
||||
InputStream in = url.openStream();
|
||||
public static DataSource createDataSource(final String unitName, URL persistxml) throws IOException {
|
||||
if (persistxml == null) persistxml = DataSources.class.getResource("/persistence.xml");
|
||||
InputStream in = persistxml.openStream();
|
||||
if (in == null) return null;
|
||||
Map<String, Properties> map = loadPersistenceXml(in);
|
||||
Properties readprop = null;
|
||||
@@ -94,7 +102,40 @@ public final class DataSources {
|
||||
if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource");
|
||||
if (writeprop == null) writeprop = readprop;
|
||||
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName());
|
||||
if (DataJdbcSource.class.getName().equals(impl)) return new DataJdbcSource(unitName, readprop, writeprop);
|
||||
if (DataJdbcSource.class.getName().equals(impl)) {
|
||||
try {
|
||||
return new DataJdbcSource(unitName, persistxml, readprop, writeprop);
|
||||
} catch (RuntimeException re) {
|
||||
if (!(re.getCause() instanceof ClassNotFoundException)) throw re;
|
||||
String dbtype = null;
|
||||
{
|
||||
/* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */
|
||||
String url = readprop.getProperty(JDBC_URL);
|
||||
int pos = url.indexOf("://");
|
||||
if (pos > 0) {
|
||||
String url0 = url.substring(0, pos);
|
||||
pos = url0.lastIndexOf(':');
|
||||
if (pos > 0) dbtype = url0.substring(pos + 1);
|
||||
} else { //jdbc:oracle:thin:@localhost:1521
|
||||
String url0 = url.substring(url.indexOf(":") + 1);
|
||||
pos = url0.indexOf(':');
|
||||
if (pos > 0) dbtype = url0.substring(0, pos);
|
||||
}
|
||||
}
|
||||
if (dbtype == null) throw re;
|
||||
Iterator<SourceLoader> it = ServiceLoader.load(SourceLoader.class).iterator();
|
||||
Class dsClass = null;
|
||||
while (it.hasNext()) {
|
||||
SourceLoader loader = it.next();
|
||||
if (dbtype.equalsIgnoreCase(loader.dbtype())) {
|
||||
dsClass = loader.dataSourceClass();
|
||||
if (dsClass != null) break;
|
||||
}
|
||||
}
|
||||
if (dsClass == null) throw re;
|
||||
impl = dsClass.getName();
|
||||
}
|
||||
}
|
||||
try {
|
||||
Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl);
|
||||
for (Constructor d : ds.getConstructors()) {
|
||||
@@ -105,9 +146,11 @@ public final class DataSources {
|
||||
return (DataSource) d.newInstance(unitName, readprop);
|
||||
} else if (paramtypes.length == 3 && paramtypes[0] == String.class && paramtypes[1] == Properties.class && paramtypes[2] == Properties.class) {
|
||||
return (DataSource) d.newInstance(unitName, readprop, writeprop);
|
||||
} else if (paramtypes.length == 4 && paramtypes[0] == String.class && paramtypes[1] == URL.class && paramtypes[2] == Properties.class && paramtypes[3] == Properties.class) {
|
||||
return (DataSource) d.newInstance(unitName, persistxml, readprop, writeprop);
|
||||
}
|
||||
}
|
||||
throw new IOException("DataSource impl class (" + impl + ") have no Constructor by (Properties prop) or (String name, Properties prop) or (String name, Properties readprop, Propertieswriteprop)");
|
||||
throw new IOException("DataSource impl class (" + impl + ") have no Constructor by (Properties prop) or (String name, Properties prop) or (String name, Properties readprop, Propertieswriteprop) or (String name, URL persistxml, Properties readprop, Propertieswriteprop)");
|
||||
} catch (IOException ex) {
|
||||
throw ex;
|
||||
} catch (Exception e) {
|
||||
|
||||
2182
src/org/redkale/source/DataSqlSource.java
Normal file
2182
src/org/redkale/source/DataSqlSource.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.stream.*;
|
||||
@@ -117,6 +118,7 @@ public final class EntityCache<T> {
|
||||
return t;
|
||||
});
|
||||
this.scheduler.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
ConcurrentHashMap newmap2 = new ConcurrentHashMap();
|
||||
List<T> all2 = info.fullloader.apply(info.source, type);
|
||||
if (all2 != null) {
|
||||
@@ -126,6 +128,9 @@ public final class EntityCache<T> {
|
||||
}
|
||||
this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2);
|
||||
this.map = newmap2;
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, type + " schedule(interval=" + interval + "s) Cacheable error", t);
|
||||
}
|
||||
}, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -226,6 +231,10 @@ public final class EntityCache<T> {
|
||||
id = ((Number) id).byteValue();
|
||||
} else if (atype == double.class || atype == Double.class) {
|
||||
id = ((Number) id).doubleValue();
|
||||
} else if (atype == AtomicInteger.class) {
|
||||
id = new AtomicInteger(((Number) id).intValue());
|
||||
} else if (atype == AtomicLong.class) {
|
||||
id = new AtomicLong(((Number) id).longValue());
|
||||
}
|
||||
}
|
||||
return this.map.containsKey(id);
|
||||
@@ -299,11 +308,11 @@ public final class EntityCache<T> {
|
||||
if (filter != null) stream = stream.filter(filter);
|
||||
switch (func) {
|
||||
case AVG:
|
||||
if (attr.type() == int.class || attr.type() == Integer.class) {
|
||||
OptionalDouble rs = stream.mapToInt(x -> (Integer) attr.get(x)).average();
|
||||
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
|
||||
OptionalDouble rs = stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).average();
|
||||
return rs.isPresent() ? (int) rs.getAsDouble() : defResult;
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class) {
|
||||
OptionalDouble rs = stream.mapToLong(x -> (Long) attr.get(x)).average();
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
|
||||
OptionalDouble rs = stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).average();
|
||||
return rs.isPresent() ? (long) rs.getAsDouble() : defResult;
|
||||
} else if (attr.type() == short.class || attr.type() == Short.class) {
|
||||
OptionalDouble rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).average();
|
||||
@@ -322,11 +331,11 @@ public final class EntityCache<T> {
|
||||
return stream.map(x -> attr.get(x)).distinct().count();
|
||||
|
||||
case MAX:
|
||||
if (attr.type() == int.class || attr.type() == Integer.class) {
|
||||
OptionalInt rs = stream.mapToInt(x -> (Integer) attr.get(x)).max();
|
||||
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
|
||||
OptionalInt rs = stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).max();
|
||||
return rs.isPresent() ? rs.getAsInt() : defResult;
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class) {
|
||||
OptionalLong rs = stream.mapToLong(x -> (Long) attr.get(x)).max();
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
|
||||
OptionalLong rs = stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).max();
|
||||
return rs.isPresent() ? rs.getAsLong() : defResult;
|
||||
} else if (attr.type() == short.class || attr.type() == Short.class) {
|
||||
OptionalInt rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).max();
|
||||
@@ -341,11 +350,11 @@ public final class EntityCache<T> {
|
||||
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
|
||||
|
||||
case MIN:
|
||||
if (attr.type() == int.class || attr.type() == Integer.class) {
|
||||
OptionalInt rs = stream.mapToInt(x -> (Integer) attr.get(x)).min();
|
||||
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
|
||||
OptionalInt rs = stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).min();
|
||||
return rs.isPresent() ? rs.getAsInt() : defResult;
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class) {
|
||||
OptionalLong rs = stream.mapToLong(x -> (Long) attr.get(x)).min();
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
|
||||
OptionalLong rs = stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).min();
|
||||
return rs.isPresent() ? rs.getAsLong() : defResult;
|
||||
} else if (attr.type() == short.class || attr.type() == Short.class) {
|
||||
OptionalInt rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).min();
|
||||
@@ -360,10 +369,10 @@ public final class EntityCache<T> {
|
||||
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
|
||||
|
||||
case SUM:
|
||||
if (attr.type() == int.class || attr.type() == Integer.class) {
|
||||
return stream.mapToInt(x -> (Integer) attr.get(x)).sum();
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class) {
|
||||
return stream.mapToLong(x -> (Long) attr.get(x)).sum();
|
||||
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
|
||||
return stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).sum();
|
||||
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
|
||||
return stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).sum();
|
||||
} else if (attr.type() == short.class || attr.type() == Short.class) {
|
||||
return (short) stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).sum();
|
||||
} else if (attr.type() == float.class || attr.type() == Float.class) {
|
||||
@@ -628,6 +637,16 @@ public final class EntityCache<T> {
|
||||
newval = numb.doubleValue();
|
||||
} else if (ft == byte.class || ft == Byte.class) {
|
||||
newval = numb.byteValue();
|
||||
} else if (ft == AtomicInteger.class) {
|
||||
newval = new AtomicInteger(numb.intValue());
|
||||
} else if (ft == AtomicLong.class) {
|
||||
newval = new AtomicLong(numb.longValue());
|
||||
}
|
||||
} else {
|
||||
if (ft == AtomicInteger.class && newval != null && newval.getClass() != AtomicInteger.class) {
|
||||
newval = new AtomicInteger(((Number) newval).intValue());
|
||||
} else if (ft == AtomicLong.class && newval != null && newval.getClass() != AtomicLong.class) {
|
||||
newval = new AtomicLong(((Number) newval).longValue());
|
||||
}
|
||||
}
|
||||
attr.set(rs, (V) newval);
|
||||
@@ -657,9 +676,9 @@ public final class EntityCache<T> {
|
||||
final String func = sub[0].substring(0, pos);
|
||||
if ("ABS".equalsIgnoreCase(func)) {
|
||||
Function getter = null;
|
||||
if (pattr.type() == int.class || pattr.type() == Integer.class) {
|
||||
if (pattr.type() == int.class || pattr.type() == Integer.class || pattr.type() == AtomicInteger.class) {
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).intValue());
|
||||
} else if (pattr.type() == long.class || pattr.type() == Long.class) {
|
||||
} else if (pattr.type() == long.class || pattr.type() == Long.class || pattr.type() == AtomicLong.class) {
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).longValue());
|
||||
} else if (pattr.type() == float.class || pattr.type() == Float.class) {
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).floatValue());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user