182 Commits
1.9.2 ... 1.9.3

Author SHA1 Message Date
Redkale
ad567b4c40 2018-06-04 18:04:27 +08:00
Redkale
6f8aeea870 2018-06-04 17:59:21 +08:00
Redkale
87a1a7c641 2018-06-04 09:39:46 +08:00
Redkale
178f96a3b5 2018-06-04 08:24:48 +08:00
Redkale
cc629a1a7c 2018-06-02 22:47:13 +08:00
Redkale
1cb72df52e 2018-06-02 22:34:34 +08:00
Redkale
4c32422493 RestWebSocket增加wsthreads属性配置 2018-06-02 15:49:10 +08:00
Redkale
7361823ece 2018-06-02 09:17:34 +08:00
Redkale
03824a900c 2018-06-01 18:35:56 +08:00
Redkale
c40913d690 2018-06-01 12:13:05 +08:00
Redkale
bdfdf153f7 2018-06-01 10:31:00 +08:00
Redkale
fa9e62d2a7 2018-06-01 10:00:59 +08:00
Redkale
bcca20dbbb 2018-05-31 17:12:18 +08:00
Redkale
bca42ee7b4 2018-05-31 15:21:48 +08:00
Redkale
d5f2bae47c 2018-05-31 15:15:08 +08:00
Redkale
fe1e99c67c 2018-05-31 11:34:50 +08:00
Redkale
699d55d70e 2018-05-31 10:23:16 +08:00
Redkale
3d11a95a29 2018-05-31 09:13:15 +08:00
Redkale
a983c53189 2018-05-31 09:02:55 +08:00
Redkale
0ee28f59ce 2018-05-31 08:47:49 +08:00
Redkale
96ee0584b8 2018-05-30 19:48:17 +08:00
Redkale
7352d5b8ed 2018-05-30 19:47:21 +08:00
Redkale
e79fdd887c 2018-05-30 19:43:46 +08:00
Redkale
7b5b0f34ff 2018-05-30 18:54:02 +08:00
Redkale
ae8fb992fb 2018-05-30 18:48:52 +08:00
Redkale
d5409518ed 2018-05-30 18:45:01 +08:00
Redkale
6bb2e152a5 2018-05-30 18:23:12 +08:00
Redkale
c73ebf88db 2018-05-29 09:35:15 +08:00
Redkale
bfdceec81b 2018-05-29 09:31:03 +08:00
Redkale
dd444e3a0f 2018-05-28 22:13:50 +08:00
Redkale
79de8dbbca 2018-05-28 17:38:41 +08:00
Redkale
5c6f3c3712 2018-05-28 17:27:10 +08:00
Redkale
0d0663a7de 2018-05-28 17:19:06 +08:00
Redkale
6d1c4e6c85 2018-05-28 10:10:59 +08:00
Redkale
f539633497 2018-05-28 10:01:17 +08:00
Redkale
d9ddd6d772 2018-05-28 09:59:39 +08:00
Redkale
4a2ba4e914 2018-05-26 09:58:34 +08:00
Redkale
cc864e3e69 2018-05-25 15:31:43 +08:00
Redkale
4cd5bd37d3 2018-05-24 10:09:35 +08:00
Redkale
ff14e675fa 2018-05-23 18:20:30 +08:00
Redkale
a46cb462a2 2018-05-23 09:28:33 +08:00
Redkale
8077dedd03 2018-05-22 09:50:38 +08:00
Redkale
6a707dff0c 2018-05-21 14:38:07 +08:00
Redkale
c9aa31d803 2018-05-21 13:57:25 +08:00
Redkale
4494683db2 2018-05-21 12:04:15 +08:00
Redkale
f440b2d639 2018-05-21 11:54:52 +08:00
Redkale
324d4bd94e 2018-05-20 13:14:41 +08:00
Redkale
bb82d70f5a 2018-05-20 12:34:53 +08:00
Redkale
8d1022f70f 2018-05-19 14:58:31 +08:00
Redkale
00034981ef 2018-05-19 13:07:36 +08:00
Redkale
2b3b33979c 2018-05-19 11:34:31 +08:00
Redkale
8e0788ccf3 2018-05-19 10:51:30 +08:00
Redkale
6e81541a3b 2018-05-19 10:19:41 +08:00
Redkale
6acb17da7c 2018-05-18 17:41:51 +08:00
Redkale
774286952b 2018-05-18 15:20:37 +08:00
Redkale
35a3c81391 2018-05-18 15:11:18 +08:00
Redkale
90d28eb4f4 2018-05-18 14:58:13 +08:00
Redkale
aab24fdbbe 2018-05-18 12:29:07 +08:00
Redkale
d0c24ed7a7 2018-05-18 12:25:49 +08:00
Redkale
2260906396 2018-05-17 19:51:02 +08:00
Redkale
0901195147 2018-05-17 19:49:27 +08:00
Redkale
c11a8e7bd9 2018-05-17 19:48:48 +08:00
Redkale
8176ec1b0a 2018-05-17 19:42:34 +08:00
Redkale
8f598fe4eb 2018-05-17 17:58:23 +08:00
Redkale
2fac5c853e 2018-05-17 17:46:32 +08:00
Redkale
4dbd6b57f8 2018-05-17 17:41:52 +08:00
Redkale
a5f21a99d8 2018-05-17 17:35:47 +08:00
Redkale
6f98b44471 2018-05-17 17:20:08 +08:00
Redkale
b13b07fb05 2018-05-17 17:06:57 +08:00
Redkale
1f8884a415 2018-05-12 15:46:33 +08:00
Redkale
848a30b2db 2018-05-12 14:35:34 +08:00
Redkale
350ece1533 2018-05-12 14:24:59 +08:00
Redkale
513183f55c 2018-05-11 17:38:11 +08:00
Redkale
b23434e3bb 2018-05-11 16:22:51 +08:00
Redkale
7084fb2ea2 2018-05-11 08:18:50 +08:00
Redkale
5becdf4d50 切换DataJdbcSource 2018-05-10 19:44:51 +08:00
Redkale
5aa10e9d74 切换DataJdbcSource 2018-05-10 19:39:47 +08:00
Redkale
db462ec134 2018-05-10 19:39:00 +08:00
Redkale
75e9f6162b 2018-05-10 19:22:35 +08:00
Redkale
970d530fbd 2018-05-10 10:25:33 +08:00
Redkale
13ffa2a3e5 2018-05-09 16:19:00 +08:00
Redkale
8416826827 2018-05-09 15:53:34 +08:00
Redkale
0a296ee857 2018-05-09 09:23:43 +08:00
Redkale
bb45470078 2018-05-07 17:03:39 +08:00
Redkale
d3c6ab8dc5 2018-05-07 12:44:55 +08:00
Redkale
0bf34781c5 2018-05-07 11:46:05 +08:00
Redkale
20940e82de 2018-05-06 18:23:38 +08:00
Redkale
78df572bec 2018-05-06 18:20:23 +08:00
Redkale
ffd0bec5c6 2018-05-06 16:54:46 +08:00
Redkale
62783d0765 2018-05-06 16:38:41 +08:00
Redkale
7116f33a3a 2018-05-06 07:50:50 +08:00
Redkale
a168dab8c0 2018-05-05 16:05:48 +08:00
Redkale
e3e065f36e 2018-05-05 13:23:42 +08:00
Redkale
d282321d98 2018-05-05 11:43:32 +08:00
Redkale
1abdb3b874 2018-05-05 11:22:26 +08:00
Redkale
1807287321 2018-05-05 10:29:55 +08:00
Redkale
a29652180b 2018-05-05 06:21:23 +08:00
Redkale
d4a85ac136 2018-05-04 21:03:40 +08:00
Redkale
007f635081 2018-05-04 20:30:54 +08:00
Redkale
f75e15a375 2018-05-04 20:29:40 +08:00
Redkale
508c7c4c0a 2018-05-04 20:23:08 +08:00
Redkale
7166dc0ec0 2018-05-04 17:31:45 +08:00
Redkale
1f28b7e803 2018-05-04 15:26:07 +08:00
Redkale
5d77139965 2018-05-04 15:17:59 +08:00
Redkale
126dab08b2 2018-05-04 12:53:16 +08:00
Redkale
a8175d9e19 2018-05-04 11:18:46 +08:00
Redkale
a5c11b5119 2018-05-04 10:26:44 +08:00
Redkale
bfc6b04a30 2018-05-04 08:50:17 +08:00
Redkale
a5dd878c66 2018-05-03 21:15:48 +08:00
Redkale
ed7ce80565 2018-05-03 21:04:23 +08:00
Redkale
e6b71a3975 2018-05-03 20:10:19 +08:00
Redkale
ff5049edc9 2018-05-03 15:33:38 +08:00
Redkale
a34218367b 2018-05-03 09:52:50 +08:00
Redkale
f712107fb8 2018-05-02 20:46:38 +08:00
Redkale
fb3dd6049d 2018-05-02 20:28:03 +08:00
Redkale
13bd467152 2018-05-02 14:59:57 +08:00
Redkale
d934f615ca 2018-05-02 09:22:10 +08:00
Redkale
5e5280a7fd 2018-05-02 08:28:18 +08:00
Redkale
be2a4d252f 2018-05-02 08:13:14 +08:00
Redkale
0392fd68a8 2018-05-01 12:14:52 +08:00
Redkale
e642854e99 2018-05-01 11:54:04 +08:00
Redkale
1a42292725 2018-05-01 10:58:59 +08:00
Redkale
c523a761c5 2018-04-30 14:27:15 +08:00
Redkale
a416ae564a 2018-04-30 14:14:54 +08:00
Redkale
ecd647ecc4 修复Keep-Alive的HTTP请求返回响应包中没有包含Keep-Alive的信息的 2018-04-30 09:37:17 +08:00
Redkale
50d0096492 2018-04-28 22:05:35 +08:00
Redkale
1a2d43479f 2018-04-28 21:57:34 +08:00
Redkale
2416e93004 2018-04-28 21:44:03 +08:00
Redkale
db99445878 2018-04-28 21:36:46 +08:00
Redkale
18459b71c2 2018-04-28 20:43:11 +08:00
Redkale
16cbabc79d 2018-04-28 20:06:12 +08:00
Redkale
c470d31605 2018-04-28 20:03:01 +08:00
Redkale
cdacc30633 2018-04-28 19:56:29 +08:00
Redkale
e55d4d8a78 不含#且方法不超过6个的RestService转换RestServlet采用枚举方式列出@WebServlet.value 2018-04-28 19:32:02 +08:00
Redkale
fcd3258a73 Source增加部分CompletableFuture异常写入日志的功能 2018-04-28 09:04:08 +08:00
Redkale
a8e9822381 2018-04-27 19:00:44 +08:00
Redkale
dfdb37973f 自动注册WebSocketNode 2018-04-27 17:45:56 +08:00
Redkale
3f340a3f7e 2018-04-25 11:38:05 +08:00
Redkale
e64d522707 2018-04-20 20:54:36 +08:00
Redkale
6c05a4a038 2018-04-20 08:58:27 +08:00
Redkale
5c9279cca4 增加Http响应包中Date头的可配置功能 2018-04-19 14:43:47 +08:00
Redkale
a4ae6dd9c4 2018-04-19 14:21:36 +08:00
Redkale
ace4042649 2018-04-18 18:30:03 +08:00
Redkale
76debd5c82 2018-04-18 17:19:48 +08:00
Redkale
8c0db6db53 2018-04-17 18:30:41 +08:00
Redkale
bc99f3f295 2018-04-17 14:24:41 +08:00
Redkale
826aaf0128 2018-04-17 13:46:30 +08:00
Redkale
754861e036 2018-04-17 11:27:34 +08:00
Redkale
b1bbc50472 2018-04-17 09:19:07 +08:00
Redkale
dcb6c6d3f8 2018-04-17 08:57:56 +08:00
Redkale
3f608b91e9 2018-04-17 08:45:31 +08:00
Redkale
9d488974a4 2018-04-16 21:53:57 +08:00
Redkale
1fa834f559 2018-04-16 21:51:21 +08:00
Redkale
812f83446e 2018-04-16 13:29:12 +08:00
Redkale
3497ab0c73 2018-04-16 08:31:24 +08:00
Redkale
78de131f83 2018-04-16 08:21:08 +08:00
Redkale
3a8ffa11fe 2018-04-15 21:07:03 +08:00
Redkale
dd62d82005 2018-04-14 13:38:13 +08:00
Redkale
7b9c71dcda 2018-04-14 13:13:13 +08:00
Redkale
4a518f1309 2018-04-14 12:52:20 +08:00
Redkale
3a0a717765 2018-04-14 12:50:13 +08:00
Redkale
60bdf4d137 2018-04-14 11:22:32 +08:00
Redkale
0ec329927a 2018-04-14 09:55:17 +08:00
Redkale
6a616b4652 2018-04-13 16:19:05 +08:00
Redkale
0bf7ccd701 2018-04-13 16:04:12 +08:00
Redkale
c924d936f3 2018-04-13 08:31:19 +08:00
Redkale
ef1c437191 HttpResponse的ContentType默认值可配置 2018-04-12 13:30:28 +08:00
Redkale
6e6d0529d6 2018-04-11 17:21:05 +08:00
Redkale
2ab179568f 2018-04-11 09:23:41 +08:00
Redkale
dab56caa98 2018-04-10 14:15:50 +08:00
Redkale
67afd13fbb 2018-04-10 11:35:11 +08:00
Redkale
3a26ddd539 2018-04-09 17:52:57 +08:00
Redkale
5197d63c70 RetLabel支持多语言版, RetLoader的load方法过期, 替换为loadMap 2018-04-09 17:28:26 +08:00
Redkale
3d136b843e 2018-04-09 14:38:40 +08:00
Redkale
388b7fbba7 2018-04-09 14:01:48 +08:00
Redkale
a833f20729 2018-04-09 10:12:44 +08:00
Redkale
d3c63bf35a 2018-04-04 14:03:55 +08:00
Redkale
5a0eff2b67 更新asm版本 2018-04-04 13:46:27 +08:00
Redkale
e64a75cfab 2018-04-03 16:16:10 +08:00
Redkale
2d31b8521d 2018-04-03 16:14:42 +08:00
Redkale
60225e3f5f 修复带空格的路径无法启动bat脚本的BUG 2018-04-03 15:19:02 +08:00
Redkale
aeb7b38474 Redkale 1.9.3 开始 2018-04-03 15:16:32 +08:00
93 changed files with 11594 additions and 5400 deletions

View File

@@ -4,4 +4,4 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -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

View File

@@ -4,6 +4,6 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
call %APP_HOME%\bin\shutdown.bat call "%APP_HOME%\bin\shutdown.bat"
call %APP_HOME%\bin\start.bat call "%APP_HOME%\bin\start.bat"

View File

@@ -4,4 +4,4 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -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

View File

@@ -4,5 +4,5 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* org.redkale.boot.Application java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application

View File

@@ -6,7 +6,6 @@
<shared-cache-mode>ALL</shared-cache-mode> <shared-cache-mode>ALL</shared-cache-mode>
<properties> <properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?autoReconnect=true&amp;characterEncoding=utf8"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?autoReconnect=true&amp;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.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="1234"/> <property name="javax.persistence.jdbc.password" value="1234"/>
</properties> </properties>

View File

@@ -37,11 +37,11 @@
threads 线程总数, 默认: <group>节点数*CPU核数*2 threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K; bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4 bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSecond: TCP读取超时秒数, 默认为6秒 为0表示无超时限制 readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
writeTimeoutSecond: TCP写入超时秒数, 默认为6秒 为0表示无超时限制 writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒 为0表示无超时限制
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类 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内的进程必须在同一机房或局域网内 一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
@@ -116,14 +116,16 @@
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
threads 线程数, 默认: CPU核数*8 threads 线程数, 默认: CPU核数*32
maxconns最大连接数, 小于1表示无限制 默认: 0 maxconns最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 默认: 32K; (HTTP 2.0、WebSocket必须要16k以上) bufferCapacity: ByteBuffer的初始化大小 默认: 32K; (HTTP 2.0、WebSocket必须要16k以上)
bufferPoolSize ByteBuffer池的大小默认: 线程数*4 bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2 responsePoolSize Response池的大小默认: 线程数*2
readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时 aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30 0表示永久不超时; -1表示禁止KeepAlive
writeTimeoutSecond: 操作超时秒数, 默认0 表示永久不超时 readTimeoutSeconds: 操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值也可以是aio或nio默认值为aioUDP情况下值也可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
--> -->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib=""> <server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
@@ -229,18 +231,26 @@
<!-- <!--
【节点在<server>中唯一】 【节点在<server>中唯一】
当Server为HTTP协议时, response节点才有效。 当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时使用该节点的默认值。 defcookie 节点: 当response里输出的cookie没有指定domain 和path时使用该节点的默认值。
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值 如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值 如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
例如下面例子是在Response输出header时添加两个header一个addHeader 一个setHeader 例如下面例子是在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> <response>
<contenttype plain="text/plain; charset=utf-8" json="application/json; charset=utf-8"/>
<defcookie domain="" path=""/> <defcookie domain="" path=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" /> <addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/> <setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/>
<setheader name="Access-Control-Allow-Credentials" value="true"/> <setheader name="Access-Control-Allow-Credentials" value="true"/>
<options auto="true" /> <options auto="true" />
<date period="0" />
</response> </response>
<!-- <!--
【节点在<server>中唯一】 【节点在<server>中唯一】

View File

@@ -70,7 +70,7 @@ public abstract class AnnotationVisitor {
/** /**
* The ASM API version implemented by this visitor. The value of this field * 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; protected final int api;
@@ -85,7 +85,7 @@ public abstract class AnnotationVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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) { public AnnotationVisitor(final int api) {
this(api, null); this(api, null);
@@ -96,15 +96,12 @@ public abstract class AnnotationVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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 * @param av
* the annotation visitor to which this visitor must delegate * the annotation visitor to which this visitor must delegate
* method calls. May be null. * method calls. May be null.
*/ */
public AnnotationVisitor(final int api, final AnnotationVisitor av) { public AnnotationVisitor(final int api, final AnnotationVisitor av) {
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api; this.api = api;
this.av = av; this.av = av;
} }
@@ -118,7 +115,7 @@ public abstract class AnnotationVisitor {
* the actual value, whose type must be {@link Byte}, * the actual value, whose type must be {@link Byte},
* {@link Boolean}, {@link Character}, {@link Short}, * {@link Boolean}, {@link Character}, {@link Short},
* {@link Integer} , {@link Long}, {@link Float}, {@link Double}, * {@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, * value can also be an array of byte, boolean, short, char, int,
* long, float or double values (this is equivalent to using * long, float or double values (this is equivalent to using
* {@link #visitArray visitArray} and visiting each array element * {@link #visitArray visitArray} and visiting each array element

View File

@@ -133,7 +133,7 @@ final class AnnotationWriter extends AnnotationVisitor {
*/ */
AnnotationWriter(final ClassWriter cw, final boolean named, AnnotationWriter(final ClassWriter cw, final boolean named,
final ByteVector bv, final ByteVector parent, final int offset) { final ByteVector bv, final ByteVector parent, final int offset) {
super(Opcodes.ASM5); super(Opcodes.ASM6);
this.cw = cw; this.cw = cw;
this.named = named; this.named = named;
this.bv = bv; this.bv = bv;

View File

@@ -64,7 +64,7 @@ package org.redkale.asm;
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
*/ */
public class Attribute { class Attribute {
/** /**
* The type of this attribute. * The type of this attribute.

View File

@@ -72,31 +72,6 @@ import java.io.InputStream;
*/ */
public class ClassReader { 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> * 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 * 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; 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 * 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 * 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) { public ClassReader(final byte[] b, final int off, final int len) {
this.b = b; this.b = b;
// checks the class version // checks the class version
if (readShort(off + 6) > Opcodes.V1_9) { if (readShort(off + 6) > Opcodes.V10) {
throw new IllegalArgumentException(); //throw new IllegalArgumentException();
} }
// parses the constant pool // parses the constant pool
items = new int[readUnsignedShort(off + 8)]; items = new int[readUnsignedShort(off + 8)];
@@ -234,6 +224,8 @@ public class ClassReader {
// case ClassWriter.CLASS: // case ClassWriter.CLASS:
// case ClassWriter.STR: // case ClassWriter.STR:
// case ClassWriter.MTYPE // case ClassWriter.MTYPE
// case ClassWriter.PACKAGE:
// case ClassWriter.MODULE:
default: default:
size = 3; size = 3;
break; break;
@@ -377,7 +369,9 @@ public class ClassReader {
break; break;
// case ClassWriter.STR: // case ClassWriter.STR:
// case ClassWriter.CLASS: // case ClassWriter.CLASS:
// case ClassWriter.MTYPE // case ClassWriter.MTYPE:
// case ClassWriter.MODULE:
// case ClassWriter.PACKAGE:
default: default:
item.set(tag, readUTF8(index, buf), null, null); item.set(tag, readUTF8(index, buf), null, null);
break; break;
@@ -584,11 +578,14 @@ public class ClassReader {
String enclosingOwner = null; String enclosingOwner = null;
String enclosingName = null; String enclosingName = null;
String enclosingDesc = null; String enclosingDesc = null;
String moduleMainClass = null;
int anns = 0; int anns = 0;
int ianns = 0; int ianns = 0;
int tanns = 0; int tanns = 0;
int itanns = 0; int itanns = 0;
int innerClasses = 0; int innerClasses = 0;
int module = 0;
int packages = 0;
Attribute attributes = null; Attribute attributes = null;
u = getAttributes(); u = getAttributes();
@@ -607,13 +604,11 @@ public class ClassReader {
enclosingName = readUTF8(items[item], c); enclosingName = readUTF8(items[item], c);
enclosingDesc = readUTF8(items[item] + 2, c); enclosingDesc = readUTF8(items[item] + 2, c);
} }
} else if (SIGNATURES && "Signature".equals(attrName)) { } else if ("Signature".equals(attrName)) {
signature = readUTF8(u + 8, c); signature = readUTF8(u + 8, c);
} else if (ANNOTATIONS } else if ("RuntimeVisibleAnnotations".equals(attrName)) {
&& "RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8; anns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
tanns = u + 8; tanns = u + 8;
} else if ("Deprecated".equals(attrName)) { } else if ("Deprecated".equals(attrName)) {
access |= Opcodes.ACC_DEPRECATED; access |= Opcodes.ACC_DEPRECATED;
@@ -623,12 +618,16 @@ public class ClassReader {
} else if ("SourceDebugExtension".equals(attrName)) { } else if ("SourceDebugExtension".equals(attrName)) {
int len = readInt(u + 4); int len = readInt(u + 4);
sourceDebug = readUTF(u + 8, len, new char[len]); sourceDebug = readUTF(u + 8, len, new char[len]);
} else if (ANNOTATIONS } else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
ianns = u + 8; ianns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
itanns = u + 8; 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)) { } else if ("BootstrapMethods".equals(attrName)) {
int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; int[] bootstrapMethods = new int[readUnsignedShort(u + 8)];
for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) {
@@ -657,6 +656,12 @@ public class ClassReader {
classVisitor.visitSource(sourceFile, sourceDebug); classVisitor.visitSource(sourceFile, sourceDebug);
} }
// visits the module info and associated attributes
if (module != 0) {
readModule(classVisitor, context, module,
moduleMainClass, packages);
}
// visits the outer class // visits the outer class
if (enclosingOwner != null) { if (enclosingOwner != null) {
classVisitor.visitOuterClass(enclosingOwner, enclosingName, classVisitor.visitOuterClass(enclosingOwner, enclosingName,
@@ -664,19 +669,19 @@ public class ClassReader {
} }
// visits the class annotations and type annotations // 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) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
classVisitor.visitAnnotation(readUTF8(v, 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) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
classVisitor.visitAnnotation(readUTF8(v, c), false)); classVisitor.visitAnnotation(readUTF8(v, c), false));
} }
} }
if (ANNOTATIONS && tanns != 0) { if (tanns != 0) {
for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
v = readAnnotationTarget(context, v); v = readAnnotationTarget(context, v);
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
@@ -684,7 +689,7 @@ public class ClassReader {
context.typePath, readUTF8(v, c), true)); context.typePath, readUTF8(v, c), true));
} }
} }
if (ANNOTATIONS && itanns != 0) { if (itanns != 0) {
for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
v = readAnnotationTarget(context, v); v = readAnnotationTarget(context, v);
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
@@ -726,6 +731,120 @@ public class ClassReader {
classVisitor.visitEnd(); 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. * Reads a field and makes the given visitor visit it.
* *
@@ -762,24 +881,20 @@ public class ClassReader {
if ("ConstantValue".equals(attrName)) { if ("ConstantValue".equals(attrName)) {
int item = readUnsignedShort(u + 8); int item = readUnsignedShort(u + 8);
value = item == 0 ? null : readConst(item, c); value = item == 0 ? null : readConst(item, c);
} else if (SIGNATURES && "Signature".equals(attrName)) { } else if ("Signature".equals(attrName)) {
signature = readUTF8(u + 8, c); signature = readUTF8(u + 8, c);
} else if ("Deprecated".equals(attrName)) { } else if ("Deprecated".equals(attrName)) {
access |= Opcodes.ACC_DEPRECATED; access |= Opcodes.ACC_DEPRECATED;
} else if ("Synthetic".equals(attrName)) { } else if ("Synthetic".equals(attrName)) {
access |= Opcodes.ACC_SYNTHETIC access |= Opcodes.ACC_SYNTHETIC
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
} else if (ANNOTATIONS } else if ("RuntimeVisibleAnnotations".equals(attrName)) {
&& "RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8; anns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
tanns = u + 8; tanns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
ianns = u + 8; ianns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
itanns = u + 8; itanns = u + 8;
} else { } else {
Attribute attr = readAttribute(context.attrs, attrName, u + 8, Attribute attr = readAttribute(context.attrs, attrName, u + 8,
@@ -801,19 +916,19 @@ public class ClassReader {
} }
// visits the field annotations and type annotations // 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) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
fv.visitAnnotation(readUTF8(v, 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) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
fv.visitAnnotation(readUTF8(v, c), false)); fv.visitAnnotation(readUTF8(v, c), false));
} }
} }
if (ANNOTATIONS && tanns != 0) { if (tanns != 0) {
for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
v = readAnnotationTarget(context, v); v = readAnnotationTarget(context, v);
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
@@ -821,7 +936,7 @@ public class ClassReader {
context.typePath, readUTF8(v, c), true)); context.typePath, readUTF8(v, c), true));
} }
} }
if (ANNOTATIONS && itanns != 0) { if (itanns != 0) {
for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
v = readAnnotationTarget(context, v); v = readAnnotationTarget(context, v);
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
@@ -895,32 +1010,26 @@ public class ClassReader {
exceptions[j] = readClass(exception, c); exceptions[j] = readClass(exception, c);
exception += 2; exception += 2;
} }
} else if (SIGNATURES && "Signature".equals(attrName)) { } else if ("Signature".equals(attrName)) {
signature = readUTF8(u + 8, c); signature = readUTF8(u + 8, c);
} else if ("Deprecated".equals(attrName)) { } else if ("Deprecated".equals(attrName)) {
context.access |= Opcodes.ACC_DEPRECATED; context.access |= Opcodes.ACC_DEPRECATED;
} else if (ANNOTATIONS } else if ("RuntimeVisibleAnnotations".equals(attrName)) {
&& "RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8; anns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
tanns = u + 8; tanns = u + 8;
} else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { } else if ("AnnotationDefault".equals(attrName)) {
dann = u + 8; dann = u + 8;
} else if ("Synthetic".equals(attrName)) { } else if ("Synthetic".equals(attrName)) {
context.access |= Opcodes.ACC_SYNTHETIC context.access |= Opcodes.ACC_SYNTHETIC
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
} else if (ANNOTATIONS } else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
&& "RuntimeInvisibleAnnotations".equals(attrName)) {
ianns = u + 8; ianns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
itanns = u + 8; itanns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) {
&& "RuntimeVisibleParameterAnnotations".equals(attrName)) {
mpanns = u + 8; mpanns = u + 8;
} else if (ANNOTATIONS } else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) {
&& "RuntimeInvisibleParameterAnnotations".equals(attrName)) {
impanns = u + 8; impanns = u + 8;
} else if ("MethodParameters".equals(attrName)) { } else if ("MethodParameters".equals(attrName)) {
methodParameters = u + 8; methodParameters = u + 8;
@@ -953,7 +1062,7 @@ public class ClassReader {
* access, name and descriptor can have been changed, this is not * access, name and descriptor can have been changed, this is not
* important since they are not copied as is from the reader). * important since they are not copied as is from the reader).
*/ */
if (WRITER && mv instanceof MethodWriter) { if (mv instanceof MethodWriter) {
MethodWriter mw = (MethodWriter) mv; MethodWriter mw = (MethodWriter) mv;
if (mw.cw.cr == this && signature == mw.signature) { if (mw.cw.cr == this && signature == mw.signature) {
boolean sameExceptions = false; boolean sameExceptions = false;
@@ -990,26 +1099,26 @@ public class ClassReader {
} }
// visits the method annotations // visits the method annotations
if (ANNOTATIONS && dann != 0) { if (dann != 0) {
AnnotationVisitor dv = mv.visitAnnotationDefault(); AnnotationVisitor dv = mv.visitAnnotationDefault();
readAnnotationValue(dann, c, null, dv); readAnnotationValue(dann, c, null, dv);
if (dv != null) { if (dv != null) {
dv.visitEnd(); dv.visitEnd();
} }
} }
if (ANNOTATIONS && anns != 0) { if (anns != 0) {
for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
mv.visitAnnotation(readUTF8(v, 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) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
mv.visitAnnotation(readUTF8(v, c), false)); mv.visitAnnotation(readUTF8(v, c), false));
} }
} }
if (ANNOTATIONS && tanns != 0) { if (tanns != 0) {
for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
v = readAnnotationTarget(context, v); v = readAnnotationTarget(context, v);
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
@@ -1017,7 +1126,7 @@ public class ClassReader {
context.typePath, readUTF8(v, c), true)); context.typePath, readUTF8(v, c), true));
} }
} }
if (ANNOTATIONS && itanns != 0) { if (itanns != 0) {
for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
v = readAnnotationTarget(context, v); v = readAnnotationTarget(context, v);
v = readAnnotationValues(v + 2, c, true, v = readAnnotationValues(v + 2, c, true,
@@ -1025,10 +1134,10 @@ public class ClassReader {
context.typePath, readUTF8(v, c), false)); context.typePath, readUTF8(v, c), false));
} }
} }
if (ANNOTATIONS && mpanns != 0) { if (mpanns != 0) {
readParameterAnnotations(mv, context, mpanns, true); readParameterAnnotations(mv, context, mpanns, true);
} }
if (ANNOTATIONS && impanns != 0) { if (impanns != 0) {
readParameterAnnotations(mv, context, impanns, false); readParameterAnnotations(mv, context, impanns, false);
} }
@@ -1075,7 +1184,7 @@ public class ClassReader {
int codeStart = u; int codeStart = u;
int codeEnd = u + codeLength; int codeEnd = u + codeLength;
Label[] labels = context.labels = new Label[codeLength + 2]; Label[] labels = context.labels = new Label[codeLength + 2];
readLabel(codeLength + 1, labels); createLabel(codeLength + 1, labels);
while (u < codeEnd) { while (u < codeEnd) {
int offset = u - codeStart; int offset = u - codeStart;
int opcode = b[u] & 0xFF; int opcode = b[u] & 0xFF;
@@ -1085,11 +1194,16 @@ public class ClassReader {
u += 1; u += 1;
break; break;
case ClassWriter.LABEL_INSN: 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; u += 3;
break; break;
case ClassWriter.LABELW_INSN: case ClassWriter.LABELW_INSN:
readLabel(offset + readInt(u + 1), labels); case ClassWriter.ASM_LABELW_INSN:
createLabel(offset + readInt(u + 1), labels);
u += 5; u += 5;
break; break;
case ClassWriter.WIDE_INSN: case ClassWriter.WIDE_INSN:
@@ -1104,9 +1218,9 @@ public class ClassReader {
// skips 0 to 3 padding bytes // skips 0 to 3 padding bytes
u = u + 4 - (offset & 3); u = u + 4 - (offset & 3);
// reads instruction // 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) { 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 += 4;
} }
u += 12; u += 12;
@@ -1115,9 +1229,9 @@ public class ClassReader {
// skips 0 to 3 padding bytes // skips 0 to 3 padding bytes
u = u + 4 - (offset & 3); u = u + 4 - (offset & 3);
// reads instruction // reads instruction
readLabel(offset + readInt(u), labels); createLabel(offset + readInt(u), labels);
for (int i = readInt(u + 4); i > 0; --i) { 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;
} }
u += 8; u += 8;
@@ -1147,9 +1261,9 @@ public class ClassReader {
// reads the try catch entries to find the labels, and also visits them // reads the try catch entries to find the labels, and also visits them
for (int i = readUnsignedShort(u); i > 0; --i) { for (int i = readUnsignedShort(u); i > 0; --i) {
Label start = readLabel(readUnsignedShort(u + 2), labels); Label start = createLabel(readUnsignedShort(u + 2), labels);
Label end = readLabel(readUnsignedShort(u + 4), labels); Label end = createLabel(readUnsignedShort(u + 4), labels);
Label handler = readLabel(readUnsignedShort(u + 6), labels); Label handler = createLabel(readUnsignedShort(u + 6), labels);
String type = readUTF8(items[readUnsignedShort(u + 8)], c); String type = readUTF8(items[readUnsignedShort(u + 8)], c);
mv.visitTryCatchBlock(start, end, handler, type); mv.visitTryCatchBlock(start, end, handler, type);
u += 8; u += 8;
@@ -1180,13 +1294,9 @@ public class ClassReader {
varTable = u + 8; varTable = u + 8;
for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
int label = readUnsignedShort(v + 10); int label = readUnsignedShort(v + 10);
if (labels[label] == null) { createDebugLabel(label, labels);
readLabel(label, labels).status |= Label.DEBUG;
}
label += readUnsignedShort(v + 12); label += readUnsignedShort(v + 12);
if (labels[label] == null) { createDebugLabel(label, labels);
readLabel(label, labels).status |= Label.DEBUG;
}
v += 10; v += 10;
} }
} }
@@ -1196,9 +1306,7 @@ public class ClassReader {
if ((context.flags & SKIP_DEBUG) == 0) { if ((context.flags & SKIP_DEBUG) == 0) {
for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
int label = readUnsignedShort(v + 10); int label = readUnsignedShort(v + 10);
if (labels[label] == null) { createDebugLabel(label, labels);
readLabel(label, labels).status |= Label.DEBUG;
}
Label l = labels[label]; Label l = labels[label];
while (l.line > 0) { while (l.line > 0) {
if (l.next == null) { if (l.next == null) {
@@ -1210,17 +1318,15 @@ public class ClassReader {
v += 4; v += 4;
} }
} }
} else if (ANNOTATIONS } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeVisibleTypeAnnotations".equals(attrName)) {
tanns = readTypeAnnotations(mv, context, u + 8, true); tanns = readTypeAnnotations(mv, context, u + 8, true);
ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1
: readUnsignedShort(tanns[0] + 1); : readUnsignedShort(tanns[0] + 1);
} else if (ANNOTATIONS } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
&& "RuntimeInvisibleTypeAnnotations".equals(attrName)) {
itanns = readTypeAnnotations(mv, context, u + 8, false); itanns = readTypeAnnotations(mv, context, u + 8, false);
nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1
: readUnsignedShort(itanns[0] + 1); : readUnsignedShort(itanns[0] + 1);
} else if (FRAMES && "StackMapTable".equals(attrName)) { } else if ("StackMapTable".equals(attrName)) {
if ((context.flags & SKIP_FRAMES) == 0) { if ((context.flags & SKIP_FRAMES) == 0) {
stackMap = u + 10; stackMap = u + 10;
stackMapSize = readInt(u + 4); stackMapSize = readInt(u + 4);
@@ -1244,7 +1350,7 @@ public class ClassReader {
* this by parsing the stack map table without a full decoding * this by parsing the stack map table without a full decoding
* (see below). * (see below).
*/ */
} else if (FRAMES && "StackMap".equals(attrName)) { } else if ("StackMap".equals(attrName)) {
if ((context.flags & SKIP_FRAMES) == 0) { if ((context.flags & SKIP_FRAMES) == 0) {
zip = false; zip = false;
stackMap = u + 10; stackMap = u + 10;
@@ -1273,7 +1379,7 @@ public class ClassReader {
u += 2; u += 2;
// generates the first (implicit) stack map frame // 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 * for the first explicit frame the offset is not offset_delta + 1
* but only offset_delta; setting the implicit frame offset to -1 * but only offset_delta; setting the implicit frame offset to -1
@@ -1306,14 +1412,31 @@ public class ClassReader {
int v = readUnsignedShort(i + 1); int v = readUnsignedShort(i + 1);
if (v >= 0 && v < codeLength) { if (v >= 0 && v < codeLength) {
if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { 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 // visits the instructions
int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
boolean insertFrame = false;
u = codeStart; u = codeStart;
while (u < codeEnd) { while (u < codeEnd) {
int offset = u - codeStart; int offset = u - codeStart;
@@ -1334,7 +1457,7 @@ public class ClassReader {
} }
// visits the frame for this offset, if any // visits the frame for this offset, if any
while (FRAMES && frame != null while (frame != null
&& (frame.offset == offset || frame.offset == -1)) { && (frame.offset == offset || frame.offset == -1)) {
// if there is a frame for this offset, makes the visitor visit // if there is a frame for this offset, makes the visitor visit
// it, and reads the next frame if there is one. // 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, mv.visitFrame(frame.mode, frame.localDiff, frame.local,
frame.stackCount, frame.stack); 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) { if (frameCount > 0) {
stackMap = readFrame(stackMap, zip, unzip, frame); stackMap = readFrame(stackMap, zip, unzip, frame);
@@ -1354,6 +1480,13 @@ public class ClassReader {
frame = null; 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 // visits the instruction at this offset
int opcode = b[u] & 0xFF; int opcode = b[u] & 0xFF;
@@ -1378,9 +1511,47 @@ public class ClassReader {
u += 3; u += 3;
break; break;
case ClassWriter.LABELW_INSN: case ClassWriter.LABELW_INSN:
mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]); mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
+ readInt(u + 1)]);
u += 5; u += 5;
break; 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: case ClassWriter.WIDE_INSN:
opcode = b[u + 1] & 0xFF; opcode = b[u + 1] & 0xFF;
if (opcode == Opcodes.IINC) { if (opcode == Opcodes.IINC) {
@@ -1636,8 +1807,8 @@ public class ClassReader {
for (int j = readUnsignedShort(u + 1); j > 0; --j) { for (int j = readUnsignedShort(u + 1); j > 0; --j) {
int start = readUnsignedShort(u + 3); int start = readUnsignedShort(u + 3);
int length = readUnsignedShort(u + 5); int length = readUnsignedShort(u + 5);
readLabel(start, context.labels); createLabel(start, context.labels);
readLabel(start + length, context.labels); createLabel(start + length, context.labels);
u += 6; u += 6;
} }
u += 3; u += 3;
@@ -1716,8 +1887,8 @@ public class ClassReader {
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
int start = readUnsignedShort(u); int start = readUnsignedShort(u);
int length = readUnsignedShort(u + 2); int length = readUnsignedShort(u + 2);
context.start[i] = readLabel(start, context.labels); context.start[i] = createLabel(start, context.labels);
context.end[i] = readLabel(start + length, context.labels); context.end[i] = createLabel(start + length, context.labels);
context.index[i] = readUnsignedShort(u + 4); context.index[i] = readUnsignedShort(u + 4);
u += 6; u += 6;
} }
@@ -2137,7 +2308,7 @@ public class ClassReader {
} }
} }
frame.offset += delta + 1; frame.offset += delta + 1;
readLabel(frame.offset, labels); createLabel(frame.offset, labels);
return stackMap; return stackMap;
} }
@@ -2190,7 +2361,7 @@ public class ClassReader {
v += 2; v += 2;
break; break;
default: // Uninitialized default: // Uninitialized
frame[index] = readLabel(readUnsignedShort(v), labels); frame[index] = createLabel(readUnsignedShort(v), labels);
v += 2; v += 2;
} }
return v; return v;
@@ -2216,6 +2387,39 @@ public class ClassReader {
return labels[offset]; 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. * 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); 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 * 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 * intended for {@link Attribute} sub classes, and is normally not needed by
@@ -2484,44 +2702,41 @@ public class ClassReader {
* @return the String corresponding to the specified class item. * @return the String corresponding to the specified class item.
*/ */
public String readClass(final int index, final char[] buf) { public String readClass(final int index, final char[] buf) {
// computes the start index of the CONSTANT_Class item in b return readStringish(index, buf);
// and reads the CONSTANT_Utf8 item designated by
// the first two bytes of this CONSTANT_Class item
return readUTF8(items[readUnsignedShort(index)], buf);
} }
/** /**
* Reads a CONSTANT_Module_info item in {@code b}. This method is intended * Reads a module constant pool item in {@link #b b}. <i>This method is
* for {@link Attribute} sub classes, and is normally not needed by class * intended for {@link Attribute} sub classes, and is normally not needed by
* generators or adapters. * class generators or adapters.</i>
* *
* @param index * @param index
* the start index of an unsigned short value in {@link #b b}, * the start index of an unsigned short value in {@link #b b},
* whose value is the index of a module constant pool item. * whose value is the index of a module constant pool item.
* @param buf * @param buf
* buffer to be used to read the item. This buffer must be * buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized. * sufficiently large. It is not automatically resized.
* @return the String corresponding to the specified module item. * @return the String corresponding to the specified module item.
*/ */
public String readModule(int index, char[] buf) { public String readModule(final int index, final char[] buf) {
return readUTF8(items[readUnsignedShort(index)], buf); return readStringish(index, buf);
} }
/** /**
* Reads a CONSTANT_Package_info item in {@code b}. This method is * Reads a module constant pool item in {@link #b b}. <i>This method is
* intended for {@link Attribute} sub slasses, and is normally not needed * intended for {@link Attribute} sub classes, and is normally not needed by
* by class generators or adapters. * class generators or adapters.</i>
* *
* @param index * @param index
* the start index of an unsigned short value in {@link #b b}, * 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 * @param buf
* buffer to be used to read the item. This buffer must be * buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized. * 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) { public String readPackage(final int index, final char[] buf) {
return readUTF8(items[readUnsignedShort(index)], buf); return readStringish(index, buf);
} }
/** /**
@@ -2550,8 +2765,6 @@ public class ClassReader {
case ClassWriter.DOUBLE: case ClassWriter.DOUBLE:
return Double.longBitsToDouble(readLong(index)); return Double.longBitsToDouble(readLong(index));
case ClassWriter.CLASS: case ClassWriter.CLASS:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
return Type.getObjectType(readUTF8(index, buf)); return Type.getObjectType(readUTF8(index, buf));
case ClassWriter.STR: case ClassWriter.STR:
return readUTF8(index, buf); return readUTF8(index, buf);

View File

@@ -61,7 +61,7 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java class. The methods of this class must be called in * A visitor to visit a Java class. The methods of this class must be called in
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [ * 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>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )* * <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
* <tt>visitEnd</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 * 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; protected final int api;
@@ -87,7 +87,7 @@ public abstract class ClassVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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) { public ClassVisitor(final int api) {
this(api, null); this(api, null);
@@ -98,15 +98,12 @@ public abstract class ClassVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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 * @param cv
* the class visitor to which this visitor must delegate method * the class visitor to which this visitor must delegate method
* calls. May be null. * calls. May be null.
*/ */
public ClassVisitor(final int api, final ClassVisitor cv) { public ClassVisitor(final int api, final ClassVisitor cv) {
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api; this.api = api;
this.cv = cv; 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 * Visits the enclosing class of the class. This method must be called only
* if the class has an enclosing class. * if the class has an enclosing class.
@@ -222,9 +241,6 @@ public abstract class ClassVisitor {
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (cv != null) { if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
} }

View File

@@ -87,8 +87,8 @@ public class ClassWriter extends ClassVisitor {
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map * {@link MethodVisitor#visitFrame} method are ignored, and the stack map
* frames are recomputed from the methods bytecode. The arguments of the * frames are recomputed from the methods bytecode. The arguments of the
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
* recomputed from the bytecode. In other words, computeFrames implies * recomputed from the bytecode. In other words, COMPUTE_FRAMES implies
* computeMaxs. * COMPUTE_MAXS.
* *
* @see #ClassWriter(int) * @see #ClassWriter(int)
*/ */
@@ -196,6 +196,27 @@ public class ClassWriter extends ClassVisitor {
*/ */
static final int WIDE_INSN = 17; 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. * 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. * The base value for all CONSTANT_MethodHandle constant pool items.
* Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 * 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; static final int HANDLE_BASE = 20;
@@ -433,6 +454,11 @@ public class ClassWriter extends ClassVisitor {
*/ */
private ByteVector sourceDebug; 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 * The constant pool item that contains the name of the enclosing class of
* this class. * this class.
@@ -523,25 +549,19 @@ public class ClassWriter extends ClassVisitor {
MethodWriter lastMethod; MethodWriter lastMethod;
/** /**
* <tt>true</tt> if the maximum stack size and number of local variables * Indicates what must be automatically computed.
* 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; boolean hasAsmInsns;
/**
* <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;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Static initializer // Static initializer
@@ -552,11 +572,11 @@ public class ClassWriter extends ClassVisitor {
*/ */
static { static {
int i; int i;
byte[] b = new byte[220]; byte[] b = new byte[221];
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA" + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ"; + "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSST";
for (i = 0; i < b.length; ++i) { for (i = 0; i < b.length; ++i) {
b[i] = (byte) (s.charAt(i) - 'A'); 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 // // temporary opcodes used internally by ASM - see Label and
// MethodWriter // MethodWriter
// for (i = 202; i < 220; ++i) { // for (i = 202; i < 220; ++i) {
// b[i] = LABEL_INSN; // b[i] = ASM_LABEL_INSN;
// } // }
// b[220] = ASM_LABELW_INSN;
// //
// // LDC(_W) instructions // // LDC(_W) instructions
// b[Constants.LDC] = LDC_INSN; // b[Constants.LDC] = LDC_INSN;
@@ -644,7 +665,7 @@ public class ClassWriter extends ClassVisitor {
* {@link #COMPUTE_FRAMES}. * {@link #COMPUTE_FRAMES}.
*/ */
public ClassWriter(final int flags) { public ClassWriter(final int flags) {
super(Opcodes.ASM5); super(Opcodes.ASM6);
index = 1; index = 1;
pool = new ByteVector(); pool = new ByteVector();
items = new Item[256]; items = new Item[256];
@@ -653,8 +674,9 @@ public class ClassWriter extends ClassVisitor {
key2 = new Item(); key2 = new Item();
key3 = new Item(); key3 = new Item();
key4 = new Item(); key4 = new Item();
this.computeMaxs = (flags & COMPUTE_MAXS) != 0; this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES
this.computeFrames = (flags & COMPUTE_FRAMES) != 0; : ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS
: MethodWriter.NOTHING);
} }
/** /**
@@ -684,9 +706,9 @@ public class ClassWriter extends ClassVisitor {
* @param flags * @param flags
* option flags that can be used to modify the default behavior * option flags that can be used to modify the default behavior
* of this class. <i>These option flags do not affect methods * of this class. <i>These option flags do not affect methods
* that are copied as is in the new class. This means that the * that are copied as is in the new class. This means that
* maximum stack size nor the stack frames will be computed for * neither the maximum stack size nor the stack frames will be
* these methods</i>. See {@link #COMPUTE_MAXS}, * computed for these methods</i>. See {@link #COMPUTE_MAXS},
* {@link #COMPUTE_FRAMES}. * {@link #COMPUTE_FRAMES}.
*/ */
public ClassWriter(final ClassReader classReader, final int flags) { public ClassWriter(final ClassReader classReader, final int flags) {
@@ -705,9 +727,9 @@ public class ClassWriter extends ClassVisitor {
final String[] interfaces) { final String[] interfaces) {
this.version = version; this.version = version;
this.access = access; this.access = access;
this.name = (name == null) ? 0 : newClass(name); this.name = newClass(name);
thisName = name; thisName = name;
if (ClassReader.SIGNATURES && signature != null) { if (signature != null) {
this.signature = newUTF8(signature); this.signature = newUTF8(signature);
} }
this.superName = superName == null ? 0 : newClass(superName); 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 @Override
public final void visitOuterClass(final String owner, final String name, public final void visitOuterClass(final String owner, final String name,
final String desc) { final String desc) {
@@ -743,9 +773,6 @@ public class ClassWriter extends ClassVisitor {
@Override @Override
public final AnnotationVisitor visitAnnotation(final String desc, public final AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) { final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector(); ByteVector bv = new ByteVector();
// write type, and reserve space for values count // write type, and reserve space for values count
bv.putShort(newUTF8(desc)).putShort(0); bv.putShort(newUTF8(desc)).putShort(0);
@@ -763,9 +790,6 @@ public class ClassWriter extends ClassVisitor {
@Override @Override
public final AnnotationVisitor visitTypeAnnotation(int typeRef, public final AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, final String desc, final boolean visible) { TypePath typePath, final String desc, final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector(); ByteVector bv = new ByteVector();
// write target_type and target_info // write target_type and target_info
AnnotationWriter.putTarget(typeRef, typePath, bv); 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 // and equality tests). If so we store the index of this inner class
// entry (plus one) in intVal. This hack allows duplicate detection in // entry (plus one) in intVal. This hack allows duplicate detection in
// O(1) time. // O(1) time.
Item nameItem = newClassItem(name); Item nameItem = newStringishItem(CLASS, name);
if (nameItem.intVal == 0) { if (nameItem.intVal == 0) {
++innerClassesCount; ++innerClassesCount;
innerClasses.putShort(nameItem.index); innerClasses.putShort(nameItem.index);
@@ -830,7 +854,7 @@ public class ClassWriter extends ClassVisitor {
public final MethodVisitor visitMethod(final int access, final String name, public final MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) { final String desc, final String signature, final String[] exceptions) {
return new MethodWriter(this, access, name, desc, signature, return new MethodWriter(this, access, name, desc, signature,
exceptions, computeMaxs, computeFrames); exceptions, compute);
} }
@Override @Override
@@ -874,7 +898,7 @@ public class ClassWriter extends ClassVisitor {
size += 8 + bootstrapMethods.length; size += 8 + bootstrapMethods.length;
newUTF8("BootstrapMethods"); newUTF8("BootstrapMethods");
} }
if (ClassReader.SIGNATURES && signature != 0) { if (signature != 0) {
++attributeCount; ++attributeCount;
size += 8; size += 8;
newUTF8("Signature"); newUTF8("Signature");
@@ -912,26 +936,31 @@ public class ClassWriter extends ClassVisitor {
size += 8 + innerClasses.length; size += 8 + innerClasses.length;
newUTF8("InnerClasses"); newUTF8("InnerClasses");
} }
if (ClassReader.ANNOTATIONS && anns != null) { if (anns != null) {
++attributeCount; ++attributeCount;
size += 8 + anns.getSize(); size += 8 + anns.getSize();
newUTF8("RuntimeVisibleAnnotations"); newUTF8("RuntimeVisibleAnnotations");
} }
if (ClassReader.ANNOTATIONS && ianns != null) { if (ianns != null) {
++attributeCount; ++attributeCount;
size += 8 + ianns.getSize(); size += 8 + ianns.getSize();
newUTF8("RuntimeInvisibleAnnotations"); newUTF8("RuntimeInvisibleAnnotations");
} }
if (ClassReader.ANNOTATIONS && tanns != null) { if (tanns != null) {
++attributeCount; ++attributeCount;
size += 8 + tanns.getSize(); size += 8 + tanns.getSize();
newUTF8("RuntimeVisibleTypeAnnotations"); newUTF8("RuntimeVisibleTypeAnnotations");
} }
if (ClassReader.ANNOTATIONS && itanns != null) { if (itanns != null) {
++attributeCount; ++attributeCount;
size += 8 + itanns.getSize(); size += 8 + itanns.getSize();
newUTF8("RuntimeInvisibleTypeAnnotations"); newUTF8("RuntimeInvisibleTypeAnnotations");
} }
if (moduleWriter != null) {
attributeCount += 1 + moduleWriter.attributeCount;
size += 6 + moduleWriter.size + moduleWriter.attributesSize;
newUTF8("Module");
}
if (attrs != null) { if (attrs != null) {
attributeCount += attrs.getCount(); attributeCount += attrs.getCount();
size += attrs.getSize(this, null, 0, -1, -1); size += attrs.getSize(this, null, 0, -1, -1);
@@ -968,7 +997,7 @@ public class ClassWriter extends ClassVisitor {
bootstrapMethodsCount); bootstrapMethodsCount);
out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length);
} }
if (ClassReader.SIGNATURES && signature != 0) { if (signature != 0) {
out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); out.putShort(newUTF8("Signature")).putInt(2).putShort(signature);
} }
if (sourceFile != 0) { if (sourceFile != 0) {
@@ -979,6 +1008,11 @@ public class ClassWriter extends ClassVisitor {
out.putShort(newUTF8("SourceDebugExtension")).putInt(len); out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
out.putByteArray(sourceDebug.data, 0, len); out.putByteArray(sourceDebug.data, 0, len);
} }
if (moduleWriter != null) {
out.putShort(newUTF8("Module"));
moduleWriter.put(out);
moduleWriter.putAttributes(out);
}
if (enclosingMethodOwner != 0) { if (enclosingMethodOwner != 0) {
out.putShort(newUTF8("EnclosingMethod")).putInt(4); out.putShort(newUTF8("EnclosingMethod")).putInt(4);
out.putShort(enclosingMethodOwner).putShort(enclosingMethod); out.putShort(enclosingMethodOwner).putShort(enclosingMethod);
@@ -997,41 +1031,46 @@ public class ClassWriter extends ClassVisitor {
out.putInt(innerClasses.length + 2).putShort(innerClassesCount); out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
out.putByteArray(innerClasses.data, 0, innerClasses.length); out.putByteArray(innerClasses.data, 0, innerClasses.length);
} }
if (ClassReader.ANNOTATIONS && anns != null) { if (anns != null) {
out.putShort(newUTF8("RuntimeVisibleAnnotations")); out.putShort(newUTF8("RuntimeVisibleAnnotations"));
anns.put(out); anns.put(out);
} }
if (ClassReader.ANNOTATIONS && ianns != null) { if (ianns != null) {
out.putShort(newUTF8("RuntimeInvisibleAnnotations")); out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out); ianns.put(out);
} }
if (ClassReader.ANNOTATIONS && tanns != null) { if (tanns != null) {
out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); out.putShort(newUTF8("RuntimeVisibleTypeAnnotations"));
tanns.put(out); tanns.put(out);
} }
if (ClassReader.ANNOTATIONS && itanns != null) { if (itanns != null) {
out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations"));
itanns.put(out); itanns.put(out);
} }
if (attrs != null) { if (attrs != null) {
attrs.put(this, null, 0, -1, -1, out); 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; anns = null;
ianns = null; ianns = null;
attrs = null; attrs = null;
innerClassesCount = 0; moduleWriter = null;
innerClasses = null;
bootstrapMethodsCount = 0;
bootstrapMethods = null;
firstField = null; firstField = null;
lastField = null; lastField = null;
firstMethod = null; firstMethod = null;
lastMethod = null; lastMethod = null;
computeMaxs = false; compute =
computeFrames = true; hasFrames ? MethodWriter.INSERTED_FRAMES : MethodWriter.NOTHING;
invalidFrames = false; hasAsmInsns = false;
new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); new ClassReader(out.data).accept(this,
(hasFrames ? ClassReader.EXPAND_FRAMES : 0)
| ClassReader.EXPAND_ASM_INSNS);
return toByteArray(); return toByteArray();
} }
return out.data; return out.data;
@@ -1078,16 +1117,16 @@ public class ClassWriter extends ClassVisitor {
double val = ((Double) cst).doubleValue(); double val = ((Double) cst).doubleValue();
return newDouble(val); return newDouble(val);
} else if (cst instanceof String) { } else if (cst instanceof String) {
return newString((String) cst); return newStringishItem(STR, (String) cst);
} else if (cst instanceof Type) { } else if (cst instanceof Type) {
Type t = (Type) cst; Type t = (Type) cst;
int s = t.getSort(); int s = t.getSort();
if (s == Type.OBJECT) { if (s == Type.OBJECT) {
return newClassItem(t.getInternalName()); return newStringishItem(CLASS, t.getInternalName());
} else if (s == Type.METHOD) { } else if (s == Type.METHOD) {
return newMethodTypeItem(t.getDescriptor()); return newStringishItem(MTYPE, t.getDescriptor());
} else { // s == primitive type or array } else { // s == primitive type or array
return newClassItem(t.getDescriptor()); return newStringishItem(CLASS, t.getDescriptor());
} }
} else if (cst instanceof Handle) { } else if (cst instanceof Handle) {
Handle h = (Handle) cst; 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. * 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 * @param value
* the internal name of the class. * string value of the reference.
* @return a new or already existing class reference item. * @return a new or already existing reference item.
*/ */
Item newClassItem(final String value) { Item newStringishItem(final int type, final String value) {
key2.set(CLASS, value, null, null); key2.set(type, value, null, null);
Item result = get(key2); Item result = get(key2);
if (result == null) { if (result == null) {
pool.put12(CLASS, newUTF8(value)); pool.put12(type, newUTF8(value));
result = new Item(index++, key2); result = new Item(index++, key2);
put(result); put(result);
} }
@@ -1167,72 +1207,7 @@ public class ClassWriter extends ClassVisitor {
* @return the index of a new or already existing class reference item. * @return the index of a new or already existing class reference item.
*/ */
public int newClass(final String value) { public int newClass(final String value) {
return newClassItem(value).index; return newStringishItem(CLASS, 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;
} }
/** /**
@@ -1247,7 +1222,37 @@ public class ClassWriter extends ClassVisitor {
* item. * item.
*/ */
public int newMethodType(final String methodDesc) { 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; 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 * 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 * if the constant pool already contains a similar item. <i>This method is
@@ -1628,25 +1601,6 @@ public class ClassWriter extends ClassVisitor {
return result; 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 * 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 * nothing if the constant pool already contains a similar item. <i>This

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

View File

@@ -69,7 +69,7 @@ public abstract class FieldVisitor {
/** /**
* The ASM API version implemented by this visitor. The value of this field * 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; protected final int api;
@@ -84,7 +84,7 @@ public abstract class FieldVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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) { public FieldVisitor(final int api) {
this(api, null); this(api, null);
@@ -95,15 +95,12 @@ public abstract class FieldVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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 * @param fv
* the field visitor to which this visitor must delegate method * the field visitor to which this visitor must delegate method
* calls. May be null. * calls. May be null.
*/ */
public FieldVisitor(final int api, final FieldVisitor fv) { public FieldVisitor(final int api, final FieldVisitor fv) {
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api; this.api = api;
this.fv = fv; this.fv = fv;
} }
@@ -145,9 +142,6 @@ public abstract class FieldVisitor {
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (fv != null) { if (fv != null) {
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
} }

View File

@@ -147,7 +147,7 @@ final class FieldWriter extends FieldVisitor {
*/ */
FieldWriter(final ClassWriter cw, final int access, final String name, FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) { final String desc, final String signature, final Object value) {
super(Opcodes.ASM5); super(Opcodes.ASM6);
if (cw.firstField == null) { if (cw.firstField == null) {
cw.firstField = this; cw.firstField = this;
} else { } else {
@@ -158,7 +158,7 @@ final class FieldWriter extends FieldVisitor {
this.access = access; this.access = access;
this.name = cw.newUTF8(name); this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc); this.desc = cw.newUTF8(desc);
if (ClassReader.SIGNATURES && signature != null) { if (signature != null) {
this.signature = cw.newUTF8(signature); this.signature = cw.newUTF8(signature);
} }
if (value != null) { if (value != null) {
@@ -173,9 +173,6 @@ final class FieldWriter extends FieldVisitor {
@Override @Override
public AnnotationVisitor visitAnnotation(final String desc, public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) { final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector(); ByteVector bv = new ByteVector();
// write type, and reserve space for values count // write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0); bv.putShort(cw.newUTF8(desc)).putShort(0);
@@ -193,9 +190,6 @@ final class FieldWriter extends FieldVisitor {
@Override @Override
public AnnotationVisitor visitTypeAnnotation(final int typeRef, public AnnotationVisitor visitTypeAnnotation(final int typeRef,
final TypePath typePath, final String desc, final boolean visible) { final TypePath typePath, final String desc, final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector(); ByteVector bv = new ByteVector();
// write target_type and target_info // write target_type and target_info
AnnotationWriter.putTarget(typeRef, typePath, bv); AnnotationWriter.putTarget(typeRef, typePath, bv);
@@ -249,23 +243,23 @@ final class FieldWriter extends FieldVisitor {
cw.newUTF8("Deprecated"); cw.newUTF8("Deprecated");
size += 6; size += 6;
} }
if (ClassReader.SIGNATURES && signature != 0) { if (signature != 0) {
cw.newUTF8("Signature"); cw.newUTF8("Signature");
size += 8; size += 8;
} }
if (ClassReader.ANNOTATIONS && anns != null) { if (anns != null) {
cw.newUTF8("RuntimeVisibleAnnotations"); cw.newUTF8("RuntimeVisibleAnnotations");
size += 8 + anns.getSize(); size += 8 + anns.getSize();
} }
if (ClassReader.ANNOTATIONS && ianns != null) { if (ianns != null) {
cw.newUTF8("RuntimeInvisibleAnnotations"); cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize(); size += 8 + ianns.getSize();
} }
if (ClassReader.ANNOTATIONS && tanns != null) { if (tanns != null) {
cw.newUTF8("RuntimeVisibleTypeAnnotations"); cw.newUTF8("RuntimeVisibleTypeAnnotations");
size += 8 + tanns.getSize(); size += 8 + tanns.getSize();
} }
if (ClassReader.ANNOTATIONS && itanns != null) { if (itanns != null) {
cw.newUTF8("RuntimeInvisibleTypeAnnotations"); cw.newUTF8("RuntimeInvisibleTypeAnnotations");
size += 8 + itanns.getSize(); size += 8 + itanns.getSize();
} }
@@ -299,19 +293,19 @@ final class FieldWriter extends FieldVisitor {
if ((access & Opcodes.ACC_DEPRECATED) != 0) { if ((access & Opcodes.ACC_DEPRECATED) != 0) {
++attributeCount; ++attributeCount;
} }
if (ClassReader.SIGNATURES && signature != 0) { if (signature != 0) {
++attributeCount; ++attributeCount;
} }
if (ClassReader.ANNOTATIONS && anns != null) { if (anns != null) {
++attributeCount; ++attributeCount;
} }
if (ClassReader.ANNOTATIONS && ianns != null) { if (ianns != null) {
++attributeCount; ++attributeCount;
} }
if (ClassReader.ANNOTATIONS && tanns != null) { if (tanns != null) {
++attributeCount; ++attributeCount;
} }
if (ClassReader.ANNOTATIONS && itanns != null) { if (itanns != null) {
++attributeCount; ++attributeCount;
} }
if (attrs != null) { if (attrs != null) {
@@ -331,23 +325,23 @@ final class FieldWriter extends FieldVisitor {
if ((access & Opcodes.ACC_DEPRECATED) != 0) { if ((access & Opcodes.ACC_DEPRECATED) != 0) {
out.putShort(cw.newUTF8("Deprecated")).putInt(0); out.putShort(cw.newUTF8("Deprecated")).putInt(0);
} }
if (ClassReader.SIGNATURES && signature != 0) { if (signature != 0) {
out.putShort(cw.newUTF8("Signature")); out.putShort(cw.newUTF8("Signature"));
out.putInt(2).putShort(signature); out.putInt(2).putShort(signature);
} }
if (ClassReader.ANNOTATIONS && anns != null) { if (anns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
anns.put(out); anns.put(out);
} }
if (ClassReader.ANNOTATIONS && ianns != null) { if (ianns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out); ianns.put(out);
} }
if (ClassReader.ANNOTATIONS && tanns != null) { if (tanns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
tanns.put(out); tanns.put(out);
} }
if (ClassReader.ANNOTATIONS && itanns != null) { if (itanns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
itanns.put(out); itanns.put(out);
} }

View File

@@ -63,7 +63,7 @@ package org.redkale.asm;
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
final class Frame { class Frame {
/* /*
* Frames are computed in a two steps process: during the visit of each * 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; 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 * map frame. The value of such types is a position relatively to the top of
* this stack. * this stack.
*/ */
@@ -525,7 +525,7 @@ final class Frame {
* When the stack map frames are completely computed, this field is the * When the stack map frames are completely computed, this field is the
* actual number of types in {@link #outputStack}. * actual number of types in {@link #outputStack}.
*/ */
private int outputStackTop; int outputStackTop;
/** /**
* Number of types that are initialized in the basic block. * Number of types that are initialized in the basic block.
@@ -549,6 +549,110 @@ final class Frame {
*/ */
private int[] initializations; 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. * 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 // pushes the type on the output stack
outputStack[outputStackTop++] = type; 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; int top = owner.inputStackTop + outputStackTop;
if (top > owner.outputStackMax) { if (top > owner.outputStackMax) {
owner.outputStackMax = top; owner.outputStackMax = top;
@@ -650,7 +754,7 @@ final class Frame {
* a type descriptor. * a type descriptor.
* @return the int encoding of the given type. * @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; String t;
int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
switch (desc.charAt(index)) { switch (desc.charAt(index)) {
@@ -838,7 +942,7 @@ final class Frame {
* @param maxLocals * @param maxLocals
* the maximum number of local variables of this method. * 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) { final Type[] args, final int maxLocals) {
inputLocals = new int[maxLocals]; inputLocals = new int[maxLocals];
inputStack = new int[0]; inputStack = new int[0];
@@ -981,7 +1085,7 @@ final class Frame {
case Opcodes.AALOAD: case Opcodes.AALOAD:
pop(1); pop(1);
t1 = pop(); t1 = pop();
push(ELEMENT_OF + t1); push(t1 == NULL ? t1 : ELEMENT_OF + t1);
break; break;
case Opcodes.ISTORE: case Opcodes.ISTORE:
case Opcodes.FSTORE: case Opcodes.FSTORE:
@@ -1312,7 +1416,7 @@ final class Frame {
* @return <tt>true</tt> if the input frame of the given label has been * @return <tt>true</tt> if the input frame of the given label has been
* changed by this operation. * 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; boolean changed = false;
int i, s, dim, kind, t; int i, s, dim, kind, t;

View File

@@ -99,34 +99,6 @@ public final class Handle {
*/ */
final boolean itf; 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. * Constructs a new field or method handle.

View File

@@ -80,6 +80,7 @@ final class Item {
* {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, * {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
* {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
* {@link ClassWriter#METH}, {@link ClassWriter#IMETH}, * {@link ClassWriter#METH}, {@link ClassWriter#IMETH},
* {@link ClassWriter#MODULE}, {@link ClassWriter#PACKAGE},
* {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}.
* *
* MethodHandle constant 9 variations are stored using a range of 9 values * MethodHandle constant 9 variations are stored using a range of 9 values
@@ -239,12 +240,12 @@ final class Item {
this.strVal3 = strVal3; this.strVal3 = strVal3;
switch (type) { switch (type) {
case ClassWriter.CLASS: case ClassWriter.CLASS:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
case ClassWriter.UTF8: case ClassWriter.UTF8:
case ClassWriter.STR: case ClassWriter.STR:
case ClassWriter.MTYPE: case ClassWriter.MTYPE:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
case ClassWriter.TYPE_NORMAL: case ClassWriter.TYPE_NORMAL:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
return; return;

View File

@@ -139,7 +139,8 @@ public class Label {
/** /**
* Field used to associate user information to a label. Warning: this field * 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 * 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; public Object info;
@@ -388,13 +389,12 @@ public class Label {
* the position of this label in the bytecode. * the position of this label in the bytecode.
* @param data * @param data
* the bytecode of the method. * the bytecode of the method.
* @return <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 * small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused * instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo * opcodes) using an unsigned two bytes offset. These pseudo
* instructions will need to be replaced with true instructions with * instructions will be replaced with standard bytecode instructions
* wider offsets (4 bytes instead of 2). This is done in * with wider offsets (4 bytes instead of 2), in ClassReader.
* {@link MethodWriter#resizeInstructions}.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if this label has already been resolved, or if it has not * if this label has already been resolved, or if it has not
* been created by the given code writer. * 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. * @return the first label of the series to which this label belongs.
*/ */
Label getFirst() { Label getFirst() {
return !ClassReader.FRAMES || frame == null ? this : frame.owner; return frame == null ? this : frame.owner;
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@@ -86,7 +86,7 @@ public abstract class MethodVisitor {
/** /**
* The ASM API version implemented by this visitor. The value of this field * 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; protected final int api;
@@ -101,7 +101,7 @@ public abstract class MethodVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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) { public MethodVisitor(final int api) {
this(api, null); this(api, null);
@@ -112,15 +112,12 @@ public abstract class MethodVisitor {
* *
* @param api * @param api
* the ASM API version implemented by this visitor. Must be one * 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 * @param mv
* the method visitor to which this visitor must delegate method * the method visitor to which this visitor must delegate method
* calls. May be null. * calls. May be null.
*/ */
public MethodVisitor(final int api, final MethodVisitor mv) { public MethodVisitor(final int api, final MethodVisitor mv) {
if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
throw new IllegalArgumentException();
}
this.api = api; this.api = api;
this.mv = mv; this.mv = mv;
} }
@@ -140,9 +137,6 @@ public abstract class MethodVisitor {
* allowed (see {@link Opcodes}). * allowed (see {@link Opcodes}).
*/ */
public void visitParameter(String name, int access) { public void visitParameter(String name, int access) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) { if (mv != null) {
mv.visitParameter(name, access); mv.visitParameter(name, access);
} }
@@ -209,9 +203,6 @@ public abstract class MethodVisitor {
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) { if (mv != null) {
return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); 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 * 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, public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) { 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) { if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc, itf); mv.visitMethodInsn(opcode, owner, name, desc, itf);
} }
@@ -723,9 +678,6 @@ public abstract class MethodVisitor {
*/ */
public AnnotationVisitor visitInsnAnnotation(int typeRef, public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) { if (mv != null) {
return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
} }
@@ -783,9 +735,6 @@ public abstract class MethodVisitor {
*/ */
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) { if (mv != null) {
return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
} }
@@ -854,9 +803,6 @@ public abstract class MethodVisitor {
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index, TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) { String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) { if (mv != null) {
return mv.visitLocalVariableAnnotation(typeRef, typePath, start, return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
end, index, desc, visible); end, index, desc, visible);

File diff suppressed because it is too large Load Diff

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

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

View File

@@ -1,4 +1,33 @@
/* /*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* ASM: a very small and fast Java bytecode manipulation framework * ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 INRIA, France Telecom * Copyright (c) 2000-2011 INRIA, France Telecom
* All rights reserved. * All rights reserved.
@@ -41,25 +70,22 @@ package org.redkale.asm;
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
*/ */
@SuppressWarnings("deprecation") // for Integer(int) constructor
public interface Opcodes { public interface Opcodes {
// ASM API versions // ASM API versions
int ASM4 = 4 << 16 | 0 << 8 | 0; int ASM4 = 4 << 16 | 0 << 8 | 0;
int ASM5 = 5 << 16 | 0 << 8 | 0; int ASM5 = 5 << 16 | 0 << 8 | 0;
int ASM6 = 6 << 16 | 0 << 8 | 0;
// versions // versions
int V1_1 = 3 << 16 | 45;
int V1_2 = 0 << 16 | 46;
int V1_3 = 0 << 16 | 47;
int V1_4 = 0 << 16 | 48;
int V1_5 = 0 << 16 | 49; int V1_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50; int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51; int V1_7 = 0 << 16 | 51;
int V1_8 = 0 << 16 | 52; int V1_8 = 0 << 16 | 52;
int V1_9 = 0 << 16 | 53; int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54;
// access flags // access flags
@@ -70,18 +96,23 @@ public interface Opcodes {
int ACC_FINAL = 0x0010; // class, field, method, parameter int ACC_FINAL = 0x0010; // class, field, method, parameter
int ACC_SUPER = 0x0020; // class int ACC_SUPER = 0x0020; // class
int ACC_SYNCHRONIZED = 0x0020; // method int ACC_SYNCHRONIZED = 0x0020; // method
int ACC_OPEN = 0x0020; // module
int ACC_TRANSITIVE = 0x0020; // module requires
int ACC_VOLATILE = 0x0040; // field int ACC_VOLATILE = 0x0040; // field
int ACC_BRIDGE = 0x0040; // method int ACC_BRIDGE = 0x0040; // method
int ACC_STATIC_PHASE = 0x0040; // module requires
int ACC_VARARGS = 0x0080; // method int ACC_VARARGS = 0x0080; // method
int ACC_TRANSIENT = 0x0080; // field int ACC_TRANSIENT = 0x0080; // field
int ACC_NATIVE = 0x0100; // method int ACC_NATIVE = 0x0100; // method
int ACC_INTERFACE = 0x0200; // class int ACC_INTERFACE = 0x0200; // class
int ACC_ABSTRACT = 0x0400; // class, method int ACC_ABSTRACT = 0x0400; // class, method
int ACC_STRICT = 0x0800; // 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_ANNOTATION = 0x2000; // class
int ACC_ENUM = 0x4000; // class(?) field inner 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 // ASM specific pseudo access flags
@@ -148,15 +179,17 @@ public interface Opcodes {
*/ */
int F_SAME1 = 4; int F_SAME1 = 4;
// For reference comparison purposes, construct new instances // Do not try to change the following code to use auto-boxing,
// instead of using valueOf() or autoboxing. // these values are compared by reference and not by value
Integer TOP = new Integer(0); // The constructor of Integer was deprecated in 9
Integer INTEGER = new Integer(1); // but we are stuck with it by backward compatibility
Integer FLOAT = new Integer(2); @SuppressWarnings("deprecation") Integer TOP = new Integer(0);
Integer DOUBLE = new Integer(3); @SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
Integer LONG = new Integer(4); @SuppressWarnings("deprecation") Integer FLOAT = new Integer(2);
Integer NULL = new Integer(5); @SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3);
Integer UNINITIALIZED_THIS = new Integer(6); @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) // opcodes // visit method (- = idem)

View File

@@ -406,7 +406,16 @@ public class Type {
*/ */
public static Type getReturnType(final String methodDescriptor) { public static Type getReturnType(final String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray(); 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++] != ';') {
}
}
}
} }
/** /**

View File

@@ -1,8 +1,62 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
* To change this template file, choose Tools | Templates *
* and open the template in the editor. *
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/ */
/*
*
*
*
*
*
* 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; package org.redkale.asm;
/** /**

View File

@@ -96,6 +96,11 @@ public final class Application {
*/ */
public static final String RESNAME_SERVER_EXECUTOR = Server.RESNAME_SERVER_EXECUTOR; 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地址 //本地IP地址
final InetAddress localAddress; final InetAddress localAddress;
@@ -249,8 +254,8 @@ public final class Application {
TransportStrategy strategy = null; TransportStrategy strategy = null;
int bufferCapacity = 32 * 1024; int bufferCapacity = 32 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8; int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSecond = TransportFactory.DEFAULT_READTIMEOUTSECOND; int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSecond = TransportFactory.DEFAULT_WRITETIMEOUTSECOND; int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AtomicLong createBufferCounter = new AtomicLong(); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong(); AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) { if (resources != null) {
@@ -260,8 +265,8 @@ public final class Application {
if (transportConf != null) { if (transportConf != null) {
//--------------transportBufferPool----------- //--------------transportBufferPool-----------
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 8 * 1024); bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 8 * 1024);
readTimeoutSecond = transportConf.getIntValue("readTimeoutSecond", readTimeoutSecond); readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds);
writeTimeoutSecond = transportConf.getIntValue("writeTimeoutSecond", writeTimeoutSecond); writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2); final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4); bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
final int capacity = bufferCapacity; final int capacity = bufferCapacity;
@@ -314,7 +319,7 @@ public final class Application {
return true; return true;
}); });
} }
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSecond, writeTimeoutSecond, strategy); 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")) 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_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30")); .addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30"));
@@ -356,6 +361,7 @@ public final class Application {
} }
public void init() throws Exception { 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("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
System.setProperty("net.transport.poolmaxconns", "100"); System.setProperty("net.transport.poolmaxconns", "100");
System.setProperty("net.transport.pinginterval", "30"); System.setProperty("net.transport.pinginterval", "30");
@@ -431,7 +437,8 @@ public final class Application {
if (type == Application.class) { if (type == Application.class) {
field.set(src, application); field.set(src, application);
} else if (type == ResourceFactory.class) { } 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) { } else if (type == TransportFactory.class) {
field.set(src, application.sncpTransportFactory); field.set(src, application.sncpTransportFactory);
} else if (type == NodeSncpServer.class) { } else if (type == NodeSncpServer.class) {
@@ -599,15 +606,15 @@ public final class Application {
buffer.flip(); buffer.flip();
channel.write(buffer); channel.write(buffer);
buffer.clear(); buffer.clear();
channel.configureBlocking(false); channel.configureBlocking(true);
try { try {
channel.read(buffer); channel.read(buffer);
buffer.flip(); buffer.flip();
byte[] bytes = new byte[buffer.remaining()]; byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes); buffer.get(bytes);
channel.close(); channel.close();
logger.info(new String(bytes)); logger.info("Send: " + command + ", Reply: " + new String(bytes));
Thread.sleep(500); Thread.sleep(1000);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof PortUnreachableException) { if (e instanceof PortUnreachableException) {
if ("APIDOC".equalsIgnoreCase(command)) { if ("APIDOC".equalsIgnoreCase(command)) {

View File

@@ -123,8 +123,8 @@ public class NodeHttpServer extends NodeServer {
regFactory.register(resourceName, WebSocketNode.class, nodeService); regFactory.register(resourceName, WebSocketNode.class, nodeService);
} }
resourceFactory.inject(nodeService, self); resourceFactory.inject(nodeService, self);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
field.set(src, nodeService); field.set(src, nodeService);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e); logger.log(Level.SEVERE, "WebSocketNode inject error", e);

View File

@@ -19,10 +19,9 @@ import javax.annotation.*;
import javax.persistence.Transient; import javax.persistence.Transient;
import static org.redkale.boot.Application.*; import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.bson.*;
import org.redkale.net.Filter; import org.redkale.net.Filter;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.WebSocketServlet; import org.redkale.net.http.*;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.source.*; import org.redkale.source.*;
@@ -96,6 +95,7 @@ public abstract class NodeServer {
this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader()); this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
Thread.currentThread().setContextClassLoader(this.serverClassLoader); Thread.currentThread().setContextClassLoader(this.serverClassLoader);
this.serverThread = Thread.currentThread(); this.serverThread = Thread.currentThread();
this.server.setServerClassLoader(serverClassLoader);
} }
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) { public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
@@ -198,7 +198,7 @@ public abstract class NodeServer {
} }
} }
} }
//------------------------------------- 注册Resource -------------------------------------------------------- //------------------------------------- 注册 Resource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
Resource res = field.getAnnotation(Resource.class); Resource res = field.getAnnotation(Resource.class);
@@ -221,7 +221,7 @@ public abstract class NodeServer {
} }
}, AnyValue.class, AnyValue[].class); }, AnyValue.class, AnyValue[].class);
//------------------------------------- 注册DataSource -------------------------------------------------------- //------------------------------------- 注册 DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
if (field.getAnnotation(Resource.class) == null) return; if (field.getAnnotation(Resource.class) == null) return;
@@ -231,7 +231,14 @@ public abstract class NodeServer {
boolean needinit = true; boolean needinit = true;
if (sourceConf != null) { if (sourceConf != null) {
final Class sourceType = serverClassLoader.loadClass(sourceConf.getValue("value")); 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; final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService); SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
@@ -271,8 +278,9 @@ public abstract class NodeServer {
} }
}, DataSource.class); }, DataSource.class);
//------------------------------------- 注册CacheSource -------------------------------------------------------- //------------------------------------- 注册 CacheSource --------------------------------------------------------
resourceFactory.register(new ResourceFactory.ResourceLoader() { resourceFactory.register(new ResourceFactory.ResourceLoader() {
@Override
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) { public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
try { try {
if (field.getAnnotation(Resource.class) == null) return; if (field.getAnnotation(Resource.class) == null) return;
@@ -317,10 +325,46 @@ public abstract class NodeServer {
} }
} }
@Override
public boolean autoNone() { public boolean autoNone() {
return false; return false;
} }
}, CacheSource.class); }, 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") @SuppressWarnings("unchecked")
@@ -340,7 +384,6 @@ public abstract class NodeServer {
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue; if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue; if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
if (DataCacheListener.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()); if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass); 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); 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<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter(); protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -708,9 +632,12 @@ public abstract class NodeServer {
public void shutdown() throws IOException { public void shutdown() throws IOException {
if (interceptor != null) interceptor.preShutdown(this); if (interceptor != null) interceptor.preShutdown(this);
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST);
localServices.forEach(y -> { localServices.forEach(y -> {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
if (finest) logger.finest(y + " is destroying");
y.destroy(Sncp.getConf(y)); y.destroy(Sncp.getConf(y));
if (finest) logger.finest(y + " was destroyed");
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) { if (e > 2 && sb != null) {
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR); sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);

View File

@@ -5,7 +5,13 @@
*/ */
package org.redkale.boot.watch; 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) @RestService(name = "server", catalog = "watch", repair = false)
public class ServerWatchService extends AbstractWatchService { 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;
}
} }

View File

@@ -22,8 +22,6 @@ import org.redkale.util.*;
*/ */
public class JsonByteBufferWriter extends JsonWriter { public class JsonByteBufferWriter extends JsonWriter {
protected static final Charset UTF8 = Charset.forName("UTF-8");
protected Charset charset; protected Charset charset;
private final Supplier<ByteBuffer> supplier; private final Supplier<ByteBuffer> supplier;
@@ -38,7 +36,7 @@ public class JsonByteBufferWriter extends JsonWriter {
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) { protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) {
this.tiny = tiny; this.tiny = tiny;
this.charset = UTF8.equals(charset) ? null : charset; this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
this.supplier = supplier; this.supplier = supplier;
} }

View File

@@ -12,6 +12,7 @@ import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
/** /**
@@ -33,11 +34,13 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
protected volatile long writetime; protected volatile long writetime;
//在线数
protected AtomicLong livingCounter;
//关闭数 //关闭数
protected AtomicLong closedCounter; protected AtomicLong closedCounter;
//在线数 protected Consumer<AsyncConnection> beforeCloseListener;
protected AtomicLong livingCounter;
public final long getLastReadTime() { public final long getLastReadTime() {
return readtime; return readtime;
@@ -49,17 +52,39 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public abstract boolean isTCP(); 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 getRemoteAddress();
public abstract SocketAddress getLocalAddress(); 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) { public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(srcs, 0, srcs.length, attachment, 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 @Override
public void close() throws IOException { public void close() throws IOException {
if (closedCounter != null) { if (closedCounter != null) {
@@ -84,6 +114,11 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
livingCounter.decrementAndGet(); livingCounter.decrementAndGet();
livingCounter = null; livingCounter = null;
} }
if (beforeCloseListener != null)
try {
beforeCloseListener.accept(this);
} catch (Exception io) {
}
if (attributes == null) return; if (attributes == null) return;
try { try {
for (Object obj : attributes.values()) { for (Object obj : attributes.values()) {
@@ -128,61 +163,44 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
/** /**
* 创建TCP协议客户端连接 * 创建TCP协议客户端连接
* *
* @param address 连接点子 * @param address 连接点子
* @param group 连接AsynchronousChannelGroup * @param group 连接AsynchronousChannelGroup
* @param readTimeoutSecond 读取超时秒数 * @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSecond 写入超时秒数 * @param writeTimeoutSeconds 写入超时秒数
* *
* @return 连接CompletableFuture * @return 连接CompletableFuture
*/ */
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address, public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
final int readTimeoutSecond, final int writeTimeoutSecond) { final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(group, null, address, false, readTimeoutSecond, writeTimeoutSecond); return createTCP(group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
} }
/** /**
* 创建TCP协议客户端连接 * 创建TCP协议客户端连接
* *
* @param address 连接点子 * @param address 连接点子
* @param sslContext SSLContext * @param sslContext SSLContext
* @param group 连接AsynchronousChannelGroup * @param group 连接AsynchronousChannelGroup
* @param readTimeoutSecond 读取超时秒数 * @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSecond 写入超时秒数 * @param writeTimeoutSeconds 写入超时秒数
* *
* @return 连接CompletableFuture * @return 连接CompletableFuture
*/ */
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SSLContext sslContext, public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SSLContext sslContext,
final SocketAddress address, final int readTimeoutSecond, final int writeTimeoutSecond) { final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
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<AsyncConnection> future = new CompletableFuture<>(); final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
try { try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); 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>() { channel.connect(address, null, new CompletionHandler<Void, Void>() {
@Override @Override
public void completed(Void result, Void attachment) { public void completed(Void result, Void attachment) {
if (noDelay) { future.complete(create(channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds));
try {
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
} catch (IOException e) {
}
}
future.complete(create(channel, sslContext, address, readTimeoutSecond, writeTimeoutSecond));
} }
@Override @Override
@@ -196,287 +214,6 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
return future; 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 * 通常用于 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) { 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 { 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);
}
private int readTimeoutSecond; 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);
}
private int writeTimeoutSecond; 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);
}
private final AsynchronousSocketChannel channel; 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);
}
private final SocketAddress remoteAddress; public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
public AIOTCPAsyncConnection(final AsynchronousSocketChannel ch, SSLContext sslContext, return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
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;
}
@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);
}
}
@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);
}
}
@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);
}
@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,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
} }
public static AsyncConnection create(final AsynchronousSocketChannel ch) { public static AsyncConnection create(final AsynchronousSocketChannel ch) {
return create(ch, null, 0, 0); return create(ch, null, 0, 0);
} }
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond, final int writeTimeoutSecond) { public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new AIOTCPAsyncConnection(ch, null, addr0, readTimeoutSecond, writeTimeoutSecond); 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) { public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new AIOTCPAsyncConnection(ch, sslContext, addr0, readTimeoutSecond, writeTimeoutSecond); return new TcpAioAsyncConnection(ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
} }
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final Context context) { 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);
} }
} }

View File

@@ -28,8 +28,6 @@ import org.redkale.util.*;
*/ */
public class Context { public class Context {
private static final Charset UTF8 = Charset.forName("UTF-8");
//服务启动时间 //服务启动时间
protected final long serverStartTime; protected final long serverStartTime;
@@ -57,14 +55,20 @@ public class Context {
//字符集 //字符集
protected final Charset charset; protected final Charset charset;
//最大连接数, 为0表示没限制
protected final int maxconns;
//请求内容的大小上限, 默认64K //请求内容的大小上限, 默认64K
protected final int maxbody; protected final int maxbody;
//keep alive IO读取的超时时间
protected final int aliveTimeoutSeconds;
//IO读取的超时时间 //IO读取的超时时间
protected final int readTimeoutSecond; protected final int readTimeoutSeconds;
//IO写入的超时时间 //IO写入的超时时间
protected final int writeTimeoutSecond; protected final int writeTimeoutSeconds;
//日志Logger //日志Logger
protected final Logger logger; protected final Logger logger;
@@ -78,10 +82,17 @@ public class Context {
//依赖注入工厂类 //依赖注入工厂类
protected final ResourceFactory resourceFactory; 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, 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 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.serverStartTime = serverStartTime;
this.logger = logger; this.logger = logger;
this.executor = executor; this.executor = executor;
@@ -89,13 +100,15 @@ public class Context {
this.bufferCapacity = bufferCapacity; this.bufferCapacity = bufferCapacity;
this.bufferPool = bufferPool; this.bufferPool = bufferPool;
this.responsePool = responsePool; this.responsePool = responsePool;
this.maxconns = maxconns;
this.maxbody = maxbody; this.maxbody = maxbody;
this.charset = UTF8.equals(charset) ? null : charset; this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
this.address = address; this.address = address;
this.prepare = prepare; this.prepare = prepare;
this.resourceFactory = resourceFactory; this.resourceFactory = resourceFactory;
this.readTimeoutSecond = readTimeoutSecond; this.aliveTimeoutSeconds = aliveTimeoutSeconds;
this.writeTimeoutSecond = writeTimeoutSecond; this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.jsonFactory = JsonFactory.root(); this.jsonFactory = JsonFactory.root();
this.bsonFactory = BsonFactory.root(); this.bsonFactory = BsonFactory.root();
} }
@@ -108,6 +121,10 @@ public class Context {
return sslContext; return sslContext;
} }
public int getMaxconns() {
return maxconns;
}
public int getMaxbody() { public int getMaxbody() {
return maxbody; return maxbody;
} }
@@ -144,7 +161,7 @@ public class Context {
return bufferPool; return bufferPool;
} }
public Consumer<ByteBuffer> getBufferConsumer() { protected Consumer<ByteBuffer> getBufferConsumer() {
return bufferPool; return bufferPool;
} }
@@ -152,11 +169,11 @@ public class Context {
return bufferPool.get(); return bufferPool.get();
} }
public void offerBuffer(ByteBuffer buffer) { protected void offerBuffer(ByteBuffer buffer) {
bufferPool.accept(buffer); bufferPool.accept(buffer);
} }
public void offerBuffer(ByteBuffer... buffers) { protected void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return; if (buffers == null) return;
for (ByteBuffer buffer : buffers) { for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer); bufferPool.accept(buffer);
@@ -167,12 +184,16 @@ public class Context {
return logger; return logger;
} }
public int getReadTimeoutSecond() { public int getAliveTimeoutSeconds() {
return readTimeoutSecond; return aliveTimeoutSeconds;
} }
public int getWriteTimeoutSecond() { public int getReadTimeoutSeconds() {
return writeTimeoutSecond; return readTimeoutSeconds;
}
public int getWriteTimeoutSeconds() {
return writeTimeoutSeconds;
} }
public JsonConvert getJsonConvert() { public JsonConvert getJsonConvert() {
@@ -182,4 +203,60 @@ public class Context {
public BsonConvert getBsonConvert() { public BsonConvert getBsonConvert() {
return bsonFactory.getConvert(); 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);
}
}
} }

View File

@@ -7,6 +7,7 @@ package org.redkale.net;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.*; import java.util.logging.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -27,20 +28,24 @@ public final class PrepareRunner implements Runnable {
private ByteBuffer data; 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.context = context;
this.channel = channel; this.channel = channel;
this.data = data; this.data = data;
this.response = response;
} }
@Override @Override
public void run() { public void run() {
final boolean keepalive = response != null;
final PrepareServlet prepare = context.prepare; final PrepareServlet prepare = context.prepare;
final ObjectPool<? extends Response> responsePool = context.responsePool; final ObjectPool<? extends Response> responsePool = context.responsePool;
if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了 if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
final Response response = responsePool.get(); if (response == null) response = responsePool.get();
response.init(channel);
try { try {
response.init(channel);
prepare.prepare(data, response.request, response); prepare.prepare(data, response.request, response);
} catch (Throwable t) { } catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", 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; return;
} }
final Response response = responsePool.get(); if (response == null) response = responsePool.get();
final ByteBuffer buffer = response.request.pollReadBuffer(); final ByteBuffer buffer = response.request.pollReadBuffer();
try { try {
channel.read(buffer, null, new CompletionHandler<Integer, Void>() { channel.read(buffer, keepalive ? context.getAliveTimeoutSeconds() : 0, TimeUnit.SECONDS, null,
new CompletionHandler<Integer, Void>() {
@Override @Override
public void completed(Integer count, Void attachment1) { public void completed(Integer count, Void attachment1) {
if (count < 1 && buffer.remaining() == buffer.limit()) { if (count < 1) {
try { response.request.offerReadBuffer(buffer);
response.request.offerReadBuffer(buffer); channel.dispose();// response.init(channel); 在调用之前异常
response.finish(true); response.removeChannel();
channel.close(); response.finish(true);
} catch (Exception e) {
context.logger.log(Level.FINEST, "PrepareRunner close channel erroneous on no read bytes", e);
}
return; return;
} }
// { //测试 // { //测试
@@ -83,22 +86,22 @@ public final class PrepareRunner implements Runnable {
@Override @Override
public void failed(Throwable exc, Void attachment2) { public void failed(Throwable exc, Void attachment2) {
response.request.offerReadBuffer(buffer); response.request.offerReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true); response.finish(true);
try { if (exc != null && context.logger.isLoggable(Level.FINEST)) {
channel.close(); context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, forece to close channel ", exc);
} catch (Exception e) {
} }
if (exc != null) context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, forece to close channel ", exc);
} }
}); });
} catch (Exception te) { } catch (Exception te) {
response.request.offerReadBuffer(buffer); response.request.offerReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true); response.finish(true);
try { if (te != null && context.logger.isLoggable(Level.FINEST)) {
channel.close(); context.logger.log(Level.FINEST, "Servlet read channel erroneous, forece to close channel ", te);
} catch (Exception e) {
} }
if (te != null) context.logger.log(Level.FINEST, "Servlet read channel erroneous, forece to close channel ", te);
} }
} }

View File

@@ -7,11 +7,9 @@ package org.redkale.net;
import java.io.IOException; import java.io.IOException;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.redkale.util.AnyValue;
/** /**
* 协议底层Server * 协议底层Server
@@ -32,10 +30,12 @@ public abstract class ProtocolServer {
//在线数 //在线数
protected final AtomicLong livingCounter = new AtomicLong(); protected final AtomicLong livingCounter = new AtomicLong();
protected final Context context;
//最大连接数小于1表示无限制 //最大连接数小于1表示无限制
protected int maxconns; 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; 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 <T> void setOption(SocketOption<T> name, T value) throws IOException;
public abstract void accept(); public abstract void accept() throws IOException;
public void setMaxconns(int maxconns) {
this.maxconns = maxconns;
}
public abstract void close() 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() { public long getCreateCount() {
return createCounter.longValue(); return createCounter.longValue();
@@ -66,176 +65,31 @@ public abstract class ProtocolServer {
} }
//--------------------------------------------------------------------- //---------------------------------------------------------------------
public static ProtocolServer create(String protocol, Context context) { public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) {
if ("TCP".equalsIgnoreCase(protocol)) return new ProtocolTCPServer(context); if (netimpl != null) netimpl = netimpl.trim();
if ("UDP".equalsIgnoreCase(protocol)) return new ProtocolUDPServer(context); if ("TCP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("ProtocolServer not support protocol " + protocol); if (netimpl == null || netimpl.isEmpty()) {
} return new TcpAioProtocolServer(context);
} else if ("aio".equalsIgnoreCase(netimpl)) {
private static final class ProtocolUDPServer extends ProtocolServer { return new TcpAioProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
private boolean running; return new TcpNioProtocolServer(context);
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));
} catch (Exception e) {
context.offerBuffer(buffer);
}
}
}
}.start();
try {
cdl.await();
} catch (Exception e) {
e.printStackTrace();
} }
} 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);
} }
try {
@Override if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
public void close() throws IOException { Class clazz = classLoader.loadClass(netimpl);
this.running = false; return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context);
this.serverChannel.close(); } catch (Exception e) {
} throw new RuntimeException("ProtocolServer(netimple=" + netimpl + ") newinstance error", e);
@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;
} }
} }

View File

@@ -91,7 +91,7 @@ public abstract class Request<C extends Context> {
createtime = 0; createtime = 0;
keepAlive = false; keepAlive = false;
attributes.clear(); attributes.clear();
channel = null; // close it by response channel = null; // close it by response
} }
protected <T> T setProperty(String name, T value) { protected <T> T setProperty(String name, T value) {

View File

@@ -156,6 +156,10 @@ public abstract class Response<C extends Context, R extends Request<C>> {
return bodyBufferSupplier; return bodyBufferSupplier;
} }
protected void offerBuffer(ByteBuffer... buffers) {
context.offerBuffer(buffers);
}
protected AsyncConnection removeChannel() { protected AsyncConnection removeChannel() {
AsyncConnection ch = this.channel; AsyncConnection ch = this.channel;
this.channel = null; this.channel = null;
@@ -169,28 +173,12 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected boolean recycle() { protected boolean recycle() {
if (!inited) return false; 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.output = null;
this.filter = null; this.filter = null;
this.servlet = null; this.servlet = null;
request.recycle(); request.recycle();
if (channel != null) { if (channel != null) {
if (keepAlive) { channel.dispose();
this.context.runAsync(new PrepareRunner(context, channel, null));
} else {
try {
if (channel.isOpen()) channel.close();
} catch (Exception e) {
}
}
channel = null; channel = null;
} }
this.inited = false; this.inited = false;
@@ -255,7 +243,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime)); //System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
if (kill) refuseAlive(); if (kill) refuseAlive();
this.context.responsePool.accept(this); 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) { public void finish(final byte[] bs) {

View File

@@ -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_EXECUTOR = "SERVER_EXECUTOR";
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); 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 //服务的根Servlet
protected final PrepareServlet<K, C, R, P, S> prepare; protected final PrepareServlet<K, C, R, P, S> prepare;
//ClassLoader
protected RedkaleClassLoader serverClassLoader;
//SSL //SSL
protected SSLContext sslContext; protected SSLContext sslContext;
@@ -88,17 +93,20 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//Response池大小 //Response池大小
protected int responsePoolSize; protected int responsePoolSize;
//最大连接数, 为0表示没限制
protected int maxconns;
//请求包大小的上限,单位:字节 //请求包大小的上限,单位:字节
protected int maxbody; protected int maxbody;
//Keep-Alive IO读取的超时秒数小于1视为不设置
protected int aliveTimeoutSeconds;
//IO读取的超时秒数小于1视为不设置 //IO读取的超时秒数小于1视为不设置
protected int readTimeoutSecond; protected int readTimeoutSeconds;
//IO写入 的超时秒数小于1视为不设置 //IO写入 的超时秒数小于1视为不设置
protected int writeTimeoutSecond; protected int writeTimeoutSeconds;
//最大连接数
protected int maxconns;
protected Server(long serverStartTime, String protocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) { protected Server(long serverStartTime, String protocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) {
this.serverStartTime = serverStartTime; this.serverStartTime = serverStartTime;
@@ -113,13 +121,14 @@ 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.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80));
this.charset = Charset.forName(config.getValue("charset", "UTF-8")); this.charset = Charset.forName(config.getValue("charset", "UTF-8"));
this.maxconns = config.getIntValue("maxconns", 0); this.maxconns = config.getIntValue("maxconns", 0);
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0); this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30);
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0); this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 16 * 1024); this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024); this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 32 * 1024); int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 32 * 1024);
this.bufferCapacity = bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity; 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.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2); this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort()); this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort());
@@ -205,6 +214,54 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return this.context; 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) { public void setThreads(int threads) {
int oldthreads = this.threads; int oldthreads = this.threads;
this.context.executor.setCorePoolSize(threads); this.context.executor.setCorePoolSize(threads);
@@ -220,13 +277,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public void start() throws IOException { public void start() throws IOException {
this.context = this.createContext(); this.context = this.createContext();
this.prepare.init(this.context, config); this.prepare.init(this.context, config);
this.serverChannel = ProtocolServer.create(this.protocol, context); this.serverChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
this.serverChannel.open(); this.serverChannel.open(config);
if (this.serverChannel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
serverChannel.bind(address, backlog); serverChannel.bind(address, backlog);
serverChannel.setMaxconns(this.maxconns);
serverChannel.accept(); serverChannel.accept();
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
@@ -249,6 +302,14 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
} }
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
this.serverClassLoader = serverClassLoader;
}
/** /**
* 判断是否存在Filter * 判断是否存在Filter
* *

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

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

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

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

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

View File

@@ -32,19 +32,6 @@ public final class Transport {
public static final String DEFAULT_PROTOCOL = "TCP"; public static final String DEFAULT_PROTOCOL = "TCP";
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 AtomicInteger seq = new AtomicInteger(-1);
protected final TransportFactory factory; protected final TransportFactory factory;
@@ -228,21 +215,25 @@ public final class Transport {
DatagramChannel channel = DatagramChannel.open(); DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true); channel.configureBlocking(true);
channel.connect(udpaddr); channel.connect(udpaddr);
return CompletableFuture.completedFuture(AsyncConnection.create(channel, udpaddr, true, factory.readTimeoutSecond, factory.writeTimeoutSecond)); return CompletableFuture.completedFuture(AsyncConnection.create(channel, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
} }
if (!rand) { //指定地址 if (!rand) { //指定地址
TransportNode node = findTransportNode(addr); TransportNode node = findTransportNode(addr);
if (node == null) { if (node == null) {
return AsyncConnection.createTCP(group, sslContext, addr, supportTcpNoDelay, factory.readTimeoutSecond, factory.writeTimeoutSecond); return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
} }
final BlockingQueue<AsyncConnection> queue = node.conns; final BlockingQueue<AsyncConnection> queue = node.conns;
if (!queue.isEmpty()) { if (!queue.isEmpty()) {
AsyncConnection conn; AsyncConnection conn;
while ((conn = queue.poll()) != null) { while ((conn = queue.poll()) != null) {
if (conn.isOpen()) return CompletableFuture.completedFuture(conn); if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
} }
} }
return AsyncConnection.createTCP(group, sslContext, addr, supportTcpNoDelay, factory.readTimeoutSecond, factory.writeTimeoutSecond); return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
} }
//---------------------随机取地址------------------------ //---------------------随机取地址------------------------
@@ -259,17 +250,23 @@ public final class Transport {
if (!queue.isEmpty()) { if (!queue.isEmpty()) {
AsyncConnection conn; AsyncConnection conn;
while ((conn = queue.poll()) != null) { while ((conn = queue.poll()) != null) {
if (conn.isOpen()) return CompletableFuture.completedFuture(conn); if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
} }
} }
CompletableFuture future = new CompletableFuture(); CompletableFuture future = new CompletableFuture();
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true); 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>() { channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override @Override
public void completed(Void result, TransportNode attachment) { public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0; attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSecond, factory.writeTimeoutSecond); AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) { if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose(); if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else { } else {
@@ -314,16 +311,22 @@ public final class Transport {
if (node == exclude) continue; if (node == exclude) continue;
if (future.isDone()) return future; if (future.isDone()) return future;
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true); 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>() { channel.connect(node.address, node, new CompletionHandler<Void, TransportNode>() {
@Override @Override
public void completed(Void result, TransportNode attachment) { public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0; try {
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSecond, factory.writeTimeoutSecond); attachment.disabletime = 0;
if (future.isDone()) { AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose(); if (future.isDone()) {
} else { if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
future.complete(asyncConn); } else {
future.complete(asyncConn);
}
} catch (Exception e) {
failed(e, attachment);
} }
} }
@@ -349,6 +352,8 @@ public final class Transport {
if (conn.isOpen()) { if (conn.isOpen()) {
TransportNode node = findTransportNode(conn.getRemoteAddress()); TransportNode node = findTransportNode(conn.getRemoteAddress());
if (node == null || !node.conns.offer(conn)) conn.dispose(); if (node == null || !node.conns.offer(conn)) conn.dispose();
} else {
conn.dispose();
} }
} else { } else {
conn.dispose(); conn.dispose();
@@ -413,7 +418,7 @@ public final class Transport {
public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) { public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) {
this.address = address; this.address = address;
this.disabletime = disabletime; this.disabletime = disabletime;
this.conns = new ArrayBlockingQueue<>(poolmaxconns); this.conns = new LinkedBlockingQueue<>(poolmaxconns);
} }
public int getPoolmaxconns() { public int getPoolmaxconns() {

View File

@@ -32,10 +32,10 @@ import org.redkale.util.*;
public class TransportFactory { public class TransportFactory {
@Comment("默认TCP读取超时秒数") @Comment("默认TCP读取超时秒数")
public static int DEFAULT_READTIMEOUTSECOND = 6; public static int DEFAULT_READTIMEOUTSECONDS = 6;
@Comment("默认TCP写入超时秒数") @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_POOLMAXCONNS = "poolmaxconns";
@@ -65,7 +65,7 @@ public class TransportFactory {
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>(); protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
//连接池大小 //连接池大小
protected int poolmaxconns = Integer.getInteger("net.transport.poolmaxconns", 100); 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 checkinterval = Integer.getInteger("net.transport.checkinterval", 30);
@@ -74,10 +74,10 @@ public class TransportFactory {
protected int pinginterval; protected int pinginterval;
//TCP读取超时秒数 //TCP读取超时秒数
protected int readTimeoutSecond; protected int readTimeoutSeconds;
//TCP写入超时秒数 //TCP写入超时秒数
protected int writeTimeoutSecond; protected int writeTimeoutSeconds;
//ping和检查的定时器 //ping和检查的定时器
private ScheduledThreadPoolExecutor scheduler; private ScheduledThreadPoolExecutor scheduler;
@@ -94,19 +94,19 @@ public class TransportFactory {
protected final TransportStrategy strategy; protected final TransportStrategy strategy;
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, 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.executor = executor;
this.bufferPool = bufferPool; this.bufferPool = bufferPool;
this.channelGroup = channelGroup; this.channelGroup = channelGroup;
this.sslContext = sslContext; this.sslContext = sslContext;
this.readTimeoutSecond = readTimeoutSecond; this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSecond = writeTimeoutSecond; this.writeTimeoutSeconds = writeTimeoutSeconds;
this.strategy = strategy; this.strategy = strategy;
} }
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSecond, int writeTimeoutSecond) { SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
this(executor, bufferPool, channelGroup, sslContext, readTimeoutSecond, writeTimeoutSecond, null); this(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
} }
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) { public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
@@ -124,7 +124,11 @@ public class TransportFactory {
return t; return t;
}); });
this.scheduler.scheduleAtFixedRate(() -> { this.scheduler.scheduleAtFixedRate(() -> {
checks(); try {
checks();
} catch (Throwable t) {
logger.log(Level.SEVERE, "TransportFactory schedule(interval=" + checkinterval + "s) check error", t);
}
}, checkinterval, checkinterval, TimeUnit.SECONDS); }, checkinterval, checkinterval, TimeUnit.SECONDS);
if (this.pinginterval > 0) { if (this.pinginterval > 0) {
@@ -140,14 +144,14 @@ public class TransportFactory {
} }
public static TransportFactory create(int threads) { 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) { 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, final ObjectPool<ByteBuffer> transportPool = new ObjectPool<>(new AtomicLong(), new AtomicLong(), bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> { (Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false; if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
@@ -167,35 +171,35 @@ public class TransportFactory {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(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) { 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, public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSecond, int writeTimeoutSecond) { int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSecond, writeTimeoutSecond, null); return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, null);
} }
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSecond, int writeTimeoutSecond, final TransportStrategy strategy) { int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSecond, writeTimeoutSecond, strategy); return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
} }
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, SSLContext sslContext) { 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, public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSecond, int writeTimeoutSecond) { SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSecond, writeTimeoutSecond, null); return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
} }
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, public static TransportFactory create(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) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSecond, writeTimeoutSecond, strategy); return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, strategy);
} }
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) { public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {

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

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

View File

@@ -5,15 +5,12 @@
*/ */
package org.redkale.net.http; package org.redkale.net.http;
import java.nio.ByteBuffer;
import org.redkale.asm.MethodDebugVisitor; import org.redkale.asm.MethodDebugVisitor;
import java.net.*;
import java.nio.*;
import java.nio.channels.CompletionHandler; import java.nio.channels.CompletionHandler;
import java.nio.charset.*;
import java.security.*; import java.security.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.logging.*; import java.util.function.*;
import javax.net.ssl.SSLContext;
import org.redkale.asm.*; import org.redkale.asm.*;
import static org.redkale.asm.Opcodes.*; import static org.redkale.asm.Opcodes.*;
import org.redkale.net.*; import org.redkale.net.*;
@@ -33,13 +30,8 @@ public class HttpContext extends Context {
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>(); protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
public HttpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext, public HttpContext(HttpContextConfig config) {
final int bufferCapacity, final ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, super(config);
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);
random.setSeed(Math.abs(System.nanoTime())); random.setSeed(Math.abs(System.nanoTime()));
} }
@@ -57,6 +49,21 @@ public class HttpContext extends Context {
return responsePool; 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") @SuppressWarnings("unchecked")
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) { protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
Creator<H> creator = asyncHandlerCreators.get(handlerClass); Creator<H> creator = asyncHandlerCreators.get(handlerClass);
@@ -169,4 +176,8 @@ public class HttpContext extends Context {
}.loadClass(newDynName.replace('/', '.'), bytes); }.loadClass(newDynName.replace('/', '.'), bytes);
return (Creator<H>) Creator.create(newHandlerClazz); return (Creator<H>) Creator.create(newHandlerClazz);
} }
public static class HttpContextConfig extends ContextConfig {
}
} }

View File

@@ -9,7 +9,7 @@ import java.io.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.charset.Charset; import java.nio.charset.*;
import java.util.*; import java.util.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*; import org.redkale.net.*;
@@ -31,8 +31,6 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
*/ */
public class HttpRequest extends Request<HttpContext> { public class HttpRequest extends Request<HttpContext> {
protected static final Charset UTF8 = Charset.forName("UTF-8");
public static final String SESSIONID_NAME = "JSESSIONID"; public static final String SESSIONID_NAME = "JSESSIONID";
@Comment("Method GET/POST/...") @Comment("Method GET/POST/...")
@@ -156,7 +154,9 @@ public class HttpRequest extends Request<HttpContext> {
case "Connection": case "Connection":
case "connection": case "connection":
this.connection = value; this.connection = value;
this.setKeepAlive(!"close".equalsIgnoreCase(value)); if (context.getAliveTimeoutSeconds() >= 0) {
this.setKeepAlive(!"close".equalsIgnoreCase(value));
}
break; break;
case "user-agent": case "user-agent":
header.addValue("User-Agent", value); header.addValue("User-Agent", value);
@@ -340,7 +340,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容 * @return 内容
*/ */
public String getBodyUTF8() { public String getBodyUTF8() {
return array.toString(UTF8); return array.toString(StandardCharsets.UTF_8);
} }
/** /**
@@ -352,7 +352,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容 * @return 内容
*/ */
public <T> T getBodyJson(java.lang.reflect.Type type) { 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; if (str == null || str.isEmpty()) return null;
return context.getJsonConvert().convertFrom(type, str); return context.getJsonConvert().convertFrom(type, str);
} }
@@ -367,7 +367,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容 * @return 内容
*/ */
public <T> T getBodyJson(JsonConvert convert, java.lang.reflect.Type type) { 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; if (str == null || str.isEmpty()) return null;
return convert.convertFrom(type, str); return convert.convertFrom(type, str);
} }
@@ -634,7 +634,11 @@ public class HttpRequest extends Request<HttpContext> {
public short getRequstURILastPath(short defvalue) { public short getRequstURILastPath(short defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
if (val.isEmpty()) return defvalue; if (val.isEmpty()) return defvalue;
return val.isEmpty() ? defvalue : Short.parseShort(val); try {
return Short.parseShort(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -650,7 +654,11 @@ public class HttpRequest extends Request<HttpContext> {
public short getRequstURILastPath(int radix, short defvalue) { public short getRequstURILastPath(int radix, short defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
if (val.isEmpty()) return defvalue; if (val.isEmpty()) return defvalue;
return val.isEmpty() ? defvalue : Short.parseShort(val, radix); try {
return Short.parseShort(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -664,7 +672,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public int getRequstURILastPath(int defvalue) { public int getRequstURILastPath(int defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Integer.parseInt(val); try {
return val.isEmpty() ? defvalue : Integer.parseInt(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -679,7 +691,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public int getRequstURILastPath(int radix, int defvalue) { public int getRequstURILastPath(int radix, int defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Integer.parseInt(val, radix); try {
return val.isEmpty() ? defvalue : Integer.parseInt(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -693,7 +709,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public float getRequstURILastPath(float defvalue) { public float getRequstURILastPath(float defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Float.parseFloat(val); try {
return val.isEmpty() ? defvalue : Float.parseFloat(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -707,7 +727,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public long getRequstURILastPath(long defvalue) { public long getRequstURILastPath(long defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Long.parseLong(val); try {
return val.isEmpty() ? defvalue : Long.parseLong(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -722,7 +746,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public long getRequstURILastPath(int radix, long defvalue) { public long getRequstURILastPath(int radix, long defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Long.parseLong(val, radix); try {
return val.isEmpty() ? defvalue : Long.parseLong(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -736,7 +764,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public double getRequstURILastPath(double defvalue) { public double getRequstURILastPath(double defvalue) {
String val = getRequstURILastPath(); String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Double.parseDouble(val); try {
return val.isEmpty() ? defvalue : Double.parseDouble(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -783,7 +815,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public short getRequstURIPath(String prefix, short defvalue) { public short getRequstURIPath(String prefix, short defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Short.parseShort(val); try {
return val == null ? defvalue : Short.parseShort(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -799,7 +835,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public short getRequstURIPath(int radix, String prefix, short defvalue) { public short getRequstURIPath(int radix, String prefix, short defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Short.parseShort(val, radix); try {
return val == null ? defvalue : Short.parseShort(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -815,7 +855,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public int getRequstURIPath(String prefix, int defvalue) { public int getRequstURIPath(String prefix, int defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Integer.parseInt(val); try {
return val == null ? defvalue : Integer.parseInt(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -832,7 +876,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public int getRequstURIPath(int radix, String prefix, int defvalue) { public int getRequstURIPath(int radix, String prefix, int defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Integer.parseInt(val, radix); try {
return val == null ? defvalue : Integer.parseInt(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -847,7 +895,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public float getRequstURIPath(String prefix, float defvalue) { public float getRequstURIPath(String prefix, float defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Float.parseFloat(val); try {
return val == null ? defvalue : Float.parseFloat(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -862,7 +914,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public long getRequstURIPath(String prefix, long defvalue) { public long getRequstURIPath(String prefix, long defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Long.parseLong(val); try {
return val == null ? defvalue : Long.parseLong(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -878,7 +934,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public long getRequstURIPath(int radix, String prefix, long defvalue) { public long getRequstURIPath(int radix, String prefix, long defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Long.parseLong(val, radix); try {
return val == null ? defvalue : Long.parseLong(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
} }
/** /**
@@ -893,7 +953,11 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public double getRequstURIPath(String prefix, double defvalue) { public double getRequstURIPath(String prefix, double defvalue) {
String val = getRequstURIPath(prefix, null); String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Double.parseDouble(val); try {
return val == null ? defvalue : Double.parseDouble(val);
} catch (NumberFormatException e) {
return defvalue;
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -1430,10 +1494,13 @@ public class HttpRequest extends Request<HttpContext> {
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name); org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
if (flipper == null) { if (flipper == null) {
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT; 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); int offset = getRequstURIPath("offset:", 0);
String sort = getRequstURIPath("sort:", ""); 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)) { } else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) {
flipper.setLimit(maxLimit); flipper.setLimit(maxLimit);
} }

View File

@@ -11,7 +11,8 @@ import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.nio.file.*; 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.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@@ -39,10 +40,16 @@ 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(); 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[] LINE = new byte[]{'\r', '\n'};
protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes(); protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes();
protected static final byte[] connectCloseBytes = "Connection: close\r\n".getBytes();
protected static final byte[] connectAliveBytes = "Connection: keep-alive\r\n".getBytes();
private static final Set<OpenOption> options = new HashSet<>(); private static final Set<OpenOption> options = new HashSet<>();
private static final Map<Integer, String> httpCodes = new HashMap<>(); private static final Map<Integer, String> httpCodes = new HashMap<>();
@@ -96,11 +103,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
httpCodes.put(505, "HTTP Version Not Supported"); httpCodes.put(505, "HTTP Version Not Supported");
} }
private final DateFormat gmtDateFormat = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH); private static final ZoneId ZONE_GMT = ZoneId.of("GMT");
private int status = 200; private int status = 200;
private String contentType = "text/plain; charset=utf-8"; private String contentType = "";
private long contentLength = -1; private long contentLength = -1;
@@ -111,6 +118,14 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> bufferHandler; 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 DefaultAnyValue header = new DefaultAnyValue();
private final String[][] defaultAddHeaders; private final String[][] defaultAddHeaders;
@@ -119,7 +134,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private final boolean autoOptions; private final boolean autoOptions;
private final HttpCookie defcookie; private final Supplier<byte[]> dateSupplier;
private final HttpCookie defaultCookie;
private final List<HttpRender> renders; private final List<HttpRender> renders;
@@ -131,17 +148,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle()); 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, public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) {
HttpCookie defcookie, boolean autoOptions, List< HttpRender> renders) {
super(context, request); super(context, request);
this.defaultAddHeaders = defaultAddHeaders; this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
this.defaultSetHeaders = defaultSetHeaders; this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
this.defcookie = defcookie; this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
this.autoOptions = autoOptions; this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes();
this.renders = renders; 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.hasRender = renders != null && !renders.isEmpty();
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null; this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
gmtDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); this.contentType = this.plainContentType;
} }
@Override @Override
@@ -155,7 +176,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override @Override
protected boolean recycle() { protected boolean recycle() {
boolean rs = super.recycle();
this.status = 200; this.status = 200;
this.contentLength = -1; this.contentLength = -1;
this.contentType = null; this.contentType = null;
@@ -163,7 +183,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.headsended = false; this.headsended = false;
this.header.clear(); this.header.clear();
this.bufferHandler = null; this.bufferHandler = null;
return rs; return super.recycle();
} }
@Override @Override
@@ -201,6 +221,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return this.autoOptions; return this.autoOptions;
} }
@Override
protected void offerBuffer(ByteBuffer... buffers) {
super.offerBuffer(buffers);
}
/** /**
* 增加Cookie值 * 增加Cookie值
* *
@@ -270,7 +295,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象 * @param obj 输出对象
*/ */
public void finishJson(final Object obj) { public void finishJson(final Object obj) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = obj; if (this.recycleListener != null) this.output = obj;
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), obj)); finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), obj));
} }
@@ -282,7 +307,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param objs 输出对象 * @param objs 输出对象
*/ */
public void finishMapJson(final Object... objs) { public void finishMapJson(final Object... objs) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs; if (this.recycleListener != null) this.output = objs;
finish(request.getJsonConvert().convertMapTo(getBodyBufferSupplier(), objs)); finish(request.getJsonConvert().convertMapTo(getBodyBufferSupplier(), objs));
} }
@@ -294,7 +319,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象 * @param obj 输出对象
*/ */
public void finishJson(final JsonConvert convert, final Object obj) { public void finishJson(final JsonConvert convert, final Object obj) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = obj; if (this.recycleListener != null) this.output = obj;
finish(convert.convertTo(getBodyBufferSupplier(), obj)); finish(convert.convertTo(getBodyBufferSupplier(), obj));
} }
@@ -307,7 +332,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param objs 输出对象 * @param objs 输出对象
*/ */
public void finishMapJson(final JsonConvert convert, final Object... objs) { public void finishMapJson(final JsonConvert convert, final Object... objs) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs; if (this.recycleListener != null) this.output = objs;
finish(convert.convertMapTo(getBodyBufferSupplier(), objs)); finish(convert.convertMapTo(getBodyBufferSupplier(), objs));
} }
@@ -319,7 +344,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象 * @param obj 输出对象
*/ */
public void finishJson(final Type type, final Object obj) { public void finishJson(final Type type, final Object obj) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
this.output = obj; this.output = obj;
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), type, obj)); finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), type, obj));
} }
@@ -332,7 +357,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象 * @param obj 输出对象
*/ */
public void finishJson(final JsonConvert convert, final Type type, final Object obj) { public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = obj; if (this.recycleListener != null) this.output = obj;
finish(convert.convertTo(getBodyBufferSupplier(), type, obj)); finish(convert.convertTo(getBodyBufferSupplier(), type, obj));
} }
@@ -343,7 +368,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param objs 输出对象 * @param objs 输出对象
*/ */
public void finishJson(final Object... objs) { public void finishJson(final Object... objs) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs; if (this.recycleListener != null) this.output = objs;
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), objs)); finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), objs));
} }
@@ -354,7 +379,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param ret RetResult输出对象 * @param ret RetResult输出对象
*/ */
public void finishJson(final org.redkale.service.RetResult ret) { public void finishJson(final org.redkale.service.RetResult ret) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = ret; if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) { if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())); this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
@@ -370,7 +395,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param ret RetResult输出对象 * @param ret RetResult输出对象
*/ */
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) { public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = ret; if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) { if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())); 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()); addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) { if (result.getResult() == null) {
finish(""); finish("");
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else { } else {
finish(convert, result.getResult()); finish(convert, result.getResult());
} }
@@ -494,9 +521,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
} }
if (convert instanceof JsonConvert) { if (convert instanceof JsonConvert) {
this.contentType = "application/json; charset=utf-8"; this.contentType = this.jsonContentType;
} else if (convert instanceof TextConvert) { } else if (convert instanceof TextConvert) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = this.plainContentType;
} }
if (this.recycleListener != null) this.output = obj; if (this.recycleListener != null) this.output = obj;
if (obj instanceof org.redkale.service.RetResult) { if (obj instanceof org.redkale.service.RetResult) {
@@ -589,7 +616,26 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public void finish(final byte[] bs) { public void finish(final byte[] bs) {
if (isClosed()) return; //避免重复关闭 if (isClosed()) return; //避免重复关闭
if (this.context.getBufferCapacity() >= bs.length) { 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.put(bs);
buffer.flip(); buffer.flip();
this.finish(false, buffer); this.finish(false, buffer);
@@ -835,18 +881,22 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
protected ByteBuffer createHeader() { protected ByteBuffer createHeader() {
this.headsended = true; this.headsended = true;
ByteBuffer buffer = this.pollWriteReadBuffer(); ByteBuffer buffer = this.pollWriteReadBuffer();
buffer.put(("HTTP/1.1 " + this.status + " " + (this.status == 200 ? "OK" : httpCodes.get(this.status)) + "\r\n").getBytes()); if (this.status == 200) {
buffer.put(status200Bytes);
buffer.put(("Content-Type: " + (this.contentType == null ? "text/plain; charset=utf-8" : this.contentType) + "\r\n").getBytes()); } else {
buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes());
if (this.contentLength >= 0) {
buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
} }
if (!this.request.isKeepAlive()) { if (this.contentLength >= 0) buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
buffer.put("Connection: close\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(("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes());
buffer.put(serverNameBytes); buffer.put(serverNameBytes);
if (dateSupplier != null) buffer.put(dateSupplier.get());
buffer.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes);
if (this.defaultAddHeaders != null) { if (this.defaultAddHeaders != null) {
for (String[] headers : this.defaultAddHeaders) { for (String[] headers : this.defaultAddHeaders) {
@@ -878,13 +928,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes()); buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
} }
if (request.newsessionid != null) { if (request.newsessionid != null) {
String domain = defcookie == null ? null : defcookie.getDomain(); String domain = defaultCookie == null ? null : defaultCookie.getDomain();
if (domain == null) { if (domain == null) {
domain = ""; domain = "";
} else { } else {
domain = "Domain=" + domain + "; "; domain = "Domain=" + domain + "; ";
} }
String path = defcookie == null ? null : defcookie.getPath(); String path = defaultCookie == null ? null : defaultCookie.getPath();
if (path == null || path.isEmpty()) path = "/"; if (path == null || path.isEmpty()) path = "/";
if (request.newsessionid.isEmpty()) { if (request.newsessionid.isEmpty()) {
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes()); buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes());
@@ -895,9 +945,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (this.cookies != null) { if (this.cookies != null) {
for (HttpCookie cookie : this.cookies) { for (HttpCookie cookie : this.cookies) {
if (cookie == null) continue; if (cookie == null) continue;
if (defcookie != null) { if (defaultCookie != null) {
if (defcookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defcookie.getDomain()); if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain());
if (defcookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defcookie.getPath()); if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath());
} }
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes()); buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
} }
@@ -914,7 +964,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist()); if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist());
if (cookie.getMaxAge() > 0) { if (cookie.getMaxAge() > 0) {
sb.append("; Max-Age=").append(cookie.getMaxAge()); sb.append("; Max-Age=").append(cookie.getMaxAge());
sb.append("; Expires=").append(gmtDateFormat.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.getSecure()) sb.append("; Secure");
if (cookie.isHttpOnly()) sb.append("; HttpOnly"); if (cookie.isHttpOnly()) sb.append("; HttpOnly");
@@ -1144,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);
}
}
} }

View File

@@ -41,6 +41,11 @@ public class HttpResult<T> {
this.result = result; this.result = result;
} }
public HttpResult(String contentType, T result) {
this.contentType = contentType;
this.result = result;
}
public HttpResult<T> header(String name, Serializable value) { public HttpResult<T> header(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>(); if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value)); this.headers.put(name, String.valueOf(value));

View File

@@ -8,10 +8,17 @@ package org.redkale.net.http;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.HttpCookie; import java.net.HttpCookie;
import java.nio.ByteBuffer; 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.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.net.*; 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.net.sncp.Sncp;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
@@ -26,6 +33,10 @@ import org.redkale.util.*;
*/ */
public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpResponse, HttpServlet> { public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpResponse, HttpServlet> {
private ScheduledThreadPoolExecutor dateScheduler;
private byte[] currDateBytes;
public HttpServer() { public HttpServer() {
this(System.currentTimeMillis(), ResourceFactory.root()); this(System.currentTimeMillis(), ResourceFactory.root());
} }
@@ -43,6 +54,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
super.init(config); 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() { public List<HttpServlet> getHttpServlets() {
return this.prepare.getServlets(); 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[]> defaultAddHeaders = new ArrayList<>();
final List<String[]> defaultSetHeaders = new ArrayList<>(); final List<String[]> defaultSetHeaders = new ArrayList<>();
boolean autoOptions = false; boolean autoOptions = false;
int datePeriod = 0;
String plainContentType = null;
String jsonContentType = null;
HttpCookie defaultCookie = null; HttpCookie defaultCookie = null;
String remoteAddrHeader = null; String remoteAddrHeader = null;
if (config != 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"); AnyValue resps = config == null ? null : config.getAnyValue("response");
if (resps != null) { if (resps != null) {
AnyValue contenttypes = resps.getAnyValue("contenttype");
if (contenttypes != null) {
plainContentType = contenttypes.getValue("plain");
jsonContentType = contenttypes.getValue("json");
}
AnyValue[] addHeaders = resps.getAnyValues("addheader"); AnyValue[] addHeaders = resps.getAnyValues("addheader");
if (addHeaders.length > 0) { if (addHeaders.length > 0) {
for (AnyValue addHeader : addHeaders) { 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"); AnyValue options = resps == null ? null : resps.getAnyValue("options");
autoOptions = options != null && options.getBoolValue("auto", false); 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()][]); Supplier<byte[]> dateSupplier = null;
final String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]); if (datePeriod == 0) {
final boolean options = autoOptions; 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 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 createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong(); AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); 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); final HttpContextConfig contextConfig = new HttpContextConfig();
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie, options, ((HttpPrepareServlet) prepare).renders)); 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; return httpcontext;
} }
} }

View File

@@ -26,8 +26,6 @@ public final class MultiContext {
private static final Logger logger = Logger.getLogger(MultiContext.class.getSimpleName()); 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 String contentType;
private final InputStream in; 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) { 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.contentType = contentType == null ? "" : contentType.trim();
this.parameters = params; this.parameters = params;
this.boundary = parseBoundary(this.contentType); this.boundary = parseBoundary(this.contentType);

View File

@@ -96,7 +96,7 @@ public final class Rest {
String key = name + ":" + desc; String key = name + ":" + desc;
if (fieldmap.containsKey(key)) return null; if (fieldmap.containsKey(key)) return null;
fieldmap.put(key, fieldnames); fieldmap.put(key, fieldnames);
return new MethodVisitor(Opcodes.ASM5) { return new MethodVisitor(Opcodes.ASM6) {
@Override @Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
if (index < 1) return; if (index < 1) return;
@@ -119,7 +119,7 @@ public final class Rest {
InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class"); InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class");
if (in == null) return map; if (in == null) return map;
try { 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) { //无需理会 } catch (Exception e) { //无需理会
} }
Class superClass = clazz.getSuperclass(); Class superClass = clazz.getSuperclass();
@@ -152,14 +152,14 @@ public final class Rest {
final RestService controller = serviceType.getAnnotation(RestService.class); final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
if (controller.ignore()) return null; 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) { static String getWebModuleName(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class); final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", ""); if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "");
if (controller.ignore()) return null; 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) { static boolean isRestDyn(HttpServlet servlet) {
@@ -720,15 +720,83 @@ public final class Rest {
av0.visit("value", Type.getType(Type.getDescriptor(serviceType))); av0.visit("value", Type.getType(Type.getDescriptor(serviceType)));
av0.visitEnd(); 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 注解 { //注入 @WebServlet 注解
String urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*"; String urlpath = "";
int moduleid = controller == null ? 0 : controller.moduleid(); int moduleid = controller == null ? 0 : controller.moduleid();
boolean repair = controller == null ? true : controller.repair(); boolean repair = controller == null ? true : controller.repair();
String comment = controller == null ? "" : controller.comment(); String comment = controller == null ? "" : controller.comment();
av0 = cw.visitAnnotation(webServletDesc, true); av0 = cw.visitAnnotation(webServletDesc, true);
{ {
AnnotationVisitor av1 = av0.visitArray("value"); AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, urlpath); 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(); av1.visitEnd();
} }
av0.visit("moduleid", moduleid); av0.visit("moduleid", moduleid);
@@ -774,48 +842,6 @@ public final class Rest {
mv.visitEnd(); 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方法 //将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType); final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType);
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>(); final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
@@ -992,7 +1018,7 @@ public final class Rest {
} }
} }
av0 = mv.visitAnnotation(mappingDesc, true); 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("url", url);
av0.visit("auth", entry.auth); av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds); av0.visit("cacheseconds", entry.cacheseconds);
@@ -1802,6 +1828,22 @@ public final class Rest {
this.actionid = mapping.actionid(); this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds(); this.cacheseconds = mapping.cacheseconds();
this.comment = mapping.comment(); 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开始 public final int methodidx; // _paramtypes 的下标从0开始
@@ -1822,6 +1864,8 @@ public final class Rest {
public final int cacheseconds; public final int cacheseconds;
public final boolean existsPound; //是否包含#的参数
@RestMapping() @RestMapping()
void mapping() { //用于获取Mapping 默认值 void mapping() { //用于获取Mapping 默认值
} }

View File

@@ -10,7 +10,7 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*; import static java.lang.annotation.RetentionPolicy.*;
/** /**
* 只能依附在Service实现类的public方法上 <br> * 只能依附在Service实现类的public方法上且方法如果throws只能是IOException <br>
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService的默认路径为/hello)。 <br> * value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService的默认路径为/hello)。 <br>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -81,6 +81,13 @@ public @interface RestWebSocket {
*/ */
int wsmaxconns() default 0; int wsmaxconns() default 0;
/**
* 操作WebSocketNode对应CacheSource并发数, 为-1表示无限制为0表示系统默认值(CPU*8)
*
* @return 最大连接数
*/
int wsthreads() default 0;
/** /**
* 最大消息体长度, 小于1表示无限制 * 最大消息体长度, 小于1表示无限制
* *

View File

@@ -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() { public CompletableFuture<Boolean> onSingleRepeatConnect() {
this._engine.node.forceCloseWebSocket(getUserid()); return forceCloseWebSocket(getUserid()).thenApply((r) -> true);
} }
/** /**

View File

@@ -70,6 +70,9 @@ public class WebSocketEngine {
@Comment("最大连接数, 为0表示无限制") @Comment("最大连接数, 为0表示无限制")
protected int wsmaxconns; protected int wsmaxconns;
@Comment("操作WebSocketNode对应CacheSource并发数, 为-1表示无限制为0表示系统默认值(CPU*8)")
protected int wsthreads;
@Comment("最大消息体长度, 小于1表示无限制") @Comment("最大消息体长度, 小于1表示无限制")
protected int wsmaxbody; protected int wsmaxbody;
@@ -77,7 +80,7 @@ public class WebSocketEngine {
protected Cryptor cryptor; protected Cryptor cryptor;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval,
int wsmaxconns, int wsmaxbody, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) { int wsmaxconns, int wsthreads, int wsmaxbody, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) {
this.engineid = engineid; this.engineid = engineid;
this.single = single; this.single = single;
this.context = context; this.context = context;
@@ -85,6 +88,7 @@ public class WebSocketEngine {
this.node = node; this.node = node;
this.liveinterval = liveinterval; this.liveinterval = liveinterval;
this.wsmaxconns = wsmaxconns; this.wsmaxconns = wsmaxconns;
this.wsthreads = wsthreads;
this.wsmaxbody = wsmaxbody; this.wsmaxbody = wsmaxbody;
this.cryptor = cryptor; this.cryptor = cryptor;
this.logger = logger; this.logger = logger;
@@ -97,6 +101,7 @@ public class WebSocketEngine {
this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval)); this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
if (liveinterval <= 0) return; if (liveinterval <= 0) return;
if (props != null) this.wsmaxconns = props.getIntValue(WEBPARAM__WSMAXCONNS, this.wsmaxconns); 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 (props != null) this.wsmaxbody = props.getIntValue(WEBPARAM__WSMAXBODY, this.wsmaxbody);
if (scheduler != null) return; if (scheduler != null) return;
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
@@ -107,8 +112,12 @@ public class WebSocketEngine {
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5; long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5;
final int intervalms = liveinterval * 1000; final int intervalms = liveinterval * 1000;
scheduler.scheduleWithFixedDelay(() -> { scheduler.scheduleWithFixedDelay(() -> {
long now = System.currentTimeMillis(); try {
getLocalWebSockets().stream().filter(x -> (now - x.getLastReadTime()) > intervalms).forEach(x -> x.sendPing()); 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); }, 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"); if (logger.isLoggable(Level.FINEST)) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(wsmaxconns:" + wsmaxconns + ", delay:" + delay + "s, interval:" + liveinterval + "s) scheduler executor");
} }
@@ -135,7 +144,7 @@ public class WebSocketEngine {
} }
@Comment("从WebSocketEngine删除指定WebSocket") @Comment("从WebSocketEngine删除指定WebSocket")
void remove(WebSocket socket) { void removeThenClose(WebSocket socket) {
Serializable userid = socket._userid; Serializable userid = socket._userid;
if (single) { if (single) {
currconns.decrementAndGet(); currconns.decrementAndGet();

View File

@@ -32,9 +32,6 @@ public abstract class WebSocketNode {
@Comment("存储用户ID的key前缀") @Comment("存储用户ID的key前缀")
public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:"; 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") @Comment("存储当前SNCP节点列表的key")
public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs"; public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs";
@@ -60,17 +57,30 @@ public abstract class WebSocketNode {
//当前节点的本地WebSocketEngine //当前节点的本地WebSocketEngine
protected WebSocketEngine localEngine; protected WebSocketEngine localEngine;
protected Semaphore semaphore;
public void init(AnyValue conf) { public void init(AnyValue conf) {
if (sncpNodeAddresses != null) sncpNodeAddresses.initValueType(InetSocketAddress.class); 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) { public void destroy(AnyValue conf) {
} }
@Local
public final Semaphore getSemaphore() {
return semaphore;
}
@Local
public final void postDestroy(AnyValue conf) { public final void postDestroy(AnyValue conf) {
if (this.localEngine == null) return; if (this.localEngine == null) return;
//关掉所有本地本地WebSocket //关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid())); this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()).join());
if (sncpNodeAddresses != null && localSncpAddress != null) { if (sncpNodeAddresses != null && localSncpAddress != null) {
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, localSncpAddress); sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, localSncpAddress);
} }
@@ -135,7 +145,12 @@ public abstract class WebSocketNode {
* @return 地址列表 * @return 地址列表
*/ */
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) { 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<>(); List<InetSocketAddress> rs = new ArrayList<>();
rs.add(this.localSncpAddress); rs.add(this.localSncpAddress);
return CompletableFuture.completedFuture(rs); return CompletableFuture.completedFuture(rs);
@@ -176,7 +191,10 @@ public abstract class WebSocketNode {
if (this.localEngine != null && this.sncpNodeAddresses == null) { if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid)); 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) { if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); 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 * @return int
*/ */
@Local
public final CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) { public final CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) {
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid)); if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
@@ -208,7 +230,9 @@ public abstract class WebSocketNode {
return localFuture; return localFuture;
} }
//远程节点关闭 //远程节点关闭
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid); 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) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
@@ -230,6 +254,7 @@ public abstract class WebSocketNode {
* *
* @return WebSocketEngine * @return WebSocketEngine
*/ */
@Local
public final WebSocketEngine getLocalWebSocketEngine() { public final WebSocketEngine getLocalWebSocketEngine() {
return this.localEngine; return this.localEngine;
} }
@@ -243,6 +268,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(Object message, final Stream<? extends Serializable> userids) { public final CompletableFuture<Integer> sendMessage(Object message, final Stream<? extends Serializable> userids) {
return sendMessage((Convert) null, message, true, userids); return sendMessage((Convert) null, message, true, userids);
} }
@@ -256,6 +282,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) { public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
return sendMessage((Convert) null, message, true, userids); return sendMessage((Convert) null, message, true, userids);
} }
@@ -270,6 +297,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Stream<? extends Serializable> userids) { public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Stream<? extends Serializable> userids) {
return sendMessage(convert, message, true, userids); return sendMessage(convert, message, true, userids);
} }
@@ -284,6 +312,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Serializable... userids) { public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Serializable... userids) {
return sendMessage(convert, message, true, userids); return sendMessage(convert, message, true, userids);
} }
@@ -298,6 +327,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) { public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
return sendMessage((Convert) null, message, last, userids); return sendMessage((Convert) null, message, last, userids);
} }
@@ -312,6 +342,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) { public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
return sendMessage((Convert) null, message, last, userids); return sendMessage((Convert) null, message, last, userids);
} }
@@ -327,6 +358,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Stream<? extends Serializable> userids) { public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Stream<? extends Serializable> userids) {
Object[] array = userids.toArray(); Object[] array = userids.toArray();
Serializable[] ss = new Serializable[array.length]; Serializable[] ss = new Serializable[array.length];
@@ -347,6 +379,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) { 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 (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)); if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids));
@@ -370,6 +403,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final Object message) { public final CompletableFuture<Integer> broadcastMessage(final Object message) {
return broadcastMessage((Convert) null, message, true); return broadcastMessage((Convert) null, message, true);
} }
@@ -382,6 +416,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message) { public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message) {
return broadcastMessage(wsrange, (Convert) null, message, true); return broadcastMessage(wsrange, (Convert) null, message, true);
} }
@@ -394,6 +429,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message) { public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message) {
return broadcastMessage(convert, message, true); return broadcastMessage(convert, message, true);
} }
@@ -407,6 +443,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) { public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) {
return broadcastMessage(wsrange, convert, message, true); return broadcastMessage(wsrange, convert, message, true);
} }
@@ -419,6 +456,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) { public final CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
return broadcastMessage((Convert) null, message, last); return broadcastMessage((Convert) null, message, last);
} }
@@ -432,6 +470,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) { public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) {
return broadcastMessage(wsrange, (Convert) null, message, last); return broadcastMessage(wsrange, (Convert) null, message, last);
} }
@@ -445,6 +484,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message0, final boolean last) { public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message0, final boolean last) {
return broadcastMessage((WebSocketRange) null, convert, message0, last); return broadcastMessage((WebSocketRange) null, convert, message0, last);
} }
@@ -459,6 +499,7 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
@Local
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) { 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)); 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)); 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); final Object remoteMessage = formatRemoteMessage(message);
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(wsrange, message, last); CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(wsrange, message, last);
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY); 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) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
@@ -496,7 +539,9 @@ public abstract class WebSocketNode {
} }
//远程节点发送消息 //远程节点发送消息
final Object remoteMessage = formatRemoteMessage(message); final Object remoteMessage = formatRemoteMessage(message);
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid); 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) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (addrs == null || addrs.isEmpty()) { if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
@@ -522,4 +567,17 @@ public abstract class WebSocketNode {
if (sendConvert instanceof BinaryConvert) ((BinaryConvert) sendConvert).convertTo(message); if (sendConvert instanceof BinaryConvert) ((BinaryConvert) sendConvert).convertTo(message);
return JsonConvert.root().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();
}
} }

View File

@@ -8,7 +8,7 @@ package org.redkale.net.http;
import org.redkale.util.Utility; import org.redkale.util.Utility;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
@@ -24,8 +24,6 @@ import org.redkale.net.Cryptor;
*/ */
public final class WebSocketPacket { public final class WebSocketPacket {
private static final Charset UTF_8 = Charset.forName("UTF-8");
static final WebSocketPacket NONE = new WebSocketPacket(); static final WebSocketPacket NONE = new WebSocketPacket();
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]); public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
@@ -480,7 +478,7 @@ public final class WebSocketPacket {
if (this.type == FrameType.TEXT) { if (this.type == FrameType.TEXT) {
Convert textConvert = webSocket.getTextConvert(); Convert textConvert = webSocket.getTextConvert();
if (textConvert == null) { 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; this.receiveType = MessageType.STRING;
} else { } else {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers); this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);

View File

@@ -6,7 +6,6 @@
package org.redkale.net.http; package org.redkale.net.http;
import org.redkale.net.AsyncConnection; import org.redkale.net.AsyncConnection;
import org.redkale.net.Context;
import static org.redkale.net.http.WebSocket.*; import static org.redkale.net.http.WebSocket.*;
import org.redkale.net.http.WebSocketPacket.FrameType; import org.redkale.net.http.WebSocketPacket.FrameType;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -34,7 +33,7 @@ class WebSocketRunner implements Runnable {
private final WebSocket webSocket; private final WebSocket webSocket;
protected final Context context; protected final HttpContext context;
private ByteBuffer readBuffer; private ByteBuffer readBuffer;
@@ -42,7 +41,7 @@ class WebSocketRunner implements Runnable {
private final AtomicBoolean writing = new AtomicBoolean(); 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使用 private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
@@ -50,7 +49,7 @@ class WebSocketRunner implements Runnable {
protected long lastReadTime; 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.context = context;
this.engine = webSocket._engine; this.engine = webSocket._engine;
this.webSocket = webSocket; this.webSocket = webSocket;
@@ -64,7 +63,7 @@ class WebSocketRunner implements Runnable {
final boolean debug = context.getLogger().isLoggable(Level.FINEST); final boolean debug = context.getLogger().isLoggable(Level.FINEST);
try { try {
webSocket.onConnected(); webSocket.onConnected();
channel.setReadTimeoutSecond(300); //读取超时5分钟 channel.setReadTimeoutSeconds(300); //读取超时5分钟
if (channel.isOpen()) { if (channel.isOpen()) {
final int wsmaxbody = webSocket._engine.wsmaxbody; final int wsmaxbody = webSocket._engine.wsmaxbody;
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() { channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
@@ -80,117 +79,125 @@ class WebSocketRunner implements Runnable {
@Override @Override
public void completed(Integer count, Void attachment1) { public void completed(Integer count, Void attachment1) {
if (count < 1) { 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); closeRunner(0, "read buffer count is " + count);
return; return;
} }
if (readBuffer == null) return;
lastReadTime = System.currentTimeMillis();
readBuffer.flip();
WebSocketPacket onePacket = null;
if (unfinishPacket != null) {
if (unfinishPacket.receiveBody(webSocket, readBuffer)) { //已经接收完毕
onePacket = unfinishPacket;
unfinishPacket = null;
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
}
exBuffers.clear();
} else { //需要继续接收
readBuffer = context.pollBuffer();
channel.read(readBuffer, null, this);
return;
}
}
final List<WebSocketPacket> packets = new ArrayList<>();
if (onePacket != null) packets.add(onePacket);
try { try {
while (true) { ByteBuffer readBuf = readBuffer;
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuffer); if (readBuf == null) return; //关闭后readBuffer为null
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节 lastReadTime = System.currentTimeMillis();
if (packet != null && !packet.isReceiveFinished()) { readBuf.flip();
unfinishPacket = packet;
if (readBuffer.hasRemaining()) { WebSocketPacket onePacket = null;
exBuffers.add(readBuffer); if (unfinishPacket != null) {
readBuffer = context.pollBuffer(); if (unfinishPacket.receiveBody(webSocket, readBuf)) { //已经接收完毕
onePacket = unfinishPacket;
unfinishPacket = null;
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
} }
break; exBuffers.clear();
} else { //需要继续接收
readBuf = context.pollBuffer();
readBuffer = readBuf;
channel.read(readBuf, null, this);
return;
}
}
final List<WebSocketPacket> packets = new ArrayList<>();
if (onePacket != null) packets.add(onePacket);
try {
while (true) {
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 (readBuf.hasRemaining()) {
exBuffers.add(readBuf);
readBuf = context.pollBuffer();
readBuffer = readBuf;
}
break;
}
packets.add(packet);
if (packet == null || !readBuf.hasRemaining()) break;
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e);
webSocket.onOccurException(e, null);
}
//继续监听消息
readBuf.clear();
if (halfBytes.getValue() != null) {
readBuf.put(halfBytes.getValue());
halfBytes.setValue(null);
}
channel.read(readBuf, null, this);
//消息处理
for (final WebSocketPacket packet : packets) {
if (packet == null) {
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
failed(null, attachment1);
return;
}
if (packet.type == FrameType.TEXT) {
try {
if (packet.receiveType == WebSocketPacket.MessageType.STRING) {
webSocket.onMessage((String) packet.receiveMessage, packet.last);
} else {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, packet.receiveMessage);
} else {
webSocket.onMessage(packet.receiveMessage, packet.last);
}
}
} catch (Throwable e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.BINARY) {
try {
if (packet.receiveType == WebSocketPacket.MessageType.BYTES) {
webSocket.onMessage((byte[]) packet.receiveMessage, packet.last);
} else {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, packet.receiveMessage);
} else {
webSocket.onMessage(packet.receiveMessage, packet.last);
}
}
} catch (Throwable e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PING) {
try {
webSocket.onPing((byte[]) packet.receiveMessage);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PONG) {
try {
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by PONG FrameType : " + packet);
webSocket.onPong((byte[]) packet.receiveMessage);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
}
} else if (packet.type == FrameType.CLOSE) {
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
closeRunner(0, "received CLOSE frame-type message");
return;
} else {
context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
closeRunner(0, "received unknown frame-type message");
return;
} }
packets.add(packet);
if (packet == null || !readBuffer.hasRemaining()) break;
} }
} catch (Exception e) { } catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e); context.getLogger().log(Level.WARNING, "WebSocketRunner(userid=" + webSocket.getUserid() + ") onMessage by received error", e);
webSocket.onOccurException(e, null); closeRunner(0, "websocket-received error");
}
//继续监听消息
readBuffer.clear();
if (halfBytes.getValue() != null) {
readBuffer.put(halfBytes.getValue());
halfBytes.setValue(null);
}
channel.read(readBuffer, null, this);
//消息处理
for (final WebSocketPacket packet : packets) {
if (packet == null) {
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
failed(null, attachment1);
return;
}
if (packet.type == FrameType.TEXT) {
try {
if (packet.receiveType == WebSocketPacket.MessageType.STRING) {
webSocket.onMessage((String) packet.receiveMessage, packet.last);
} else {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, packet.receiveMessage);
} else {
webSocket.onMessage(packet.receiveMessage, packet.last);
}
}
} catch (Throwable e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.BINARY) {
try {
if (packet.receiveType == WebSocketPacket.MessageType.BYTES) {
webSocket.onMessage((byte[]) packet.receiveMessage, packet.last);
} else {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, packet.receiveMessage);
} else {
webSocket.onMessage(packet.receiveMessage, packet.last);
}
}
} catch (Throwable e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PING) {
try {
webSocket.onPing((byte[]) packet.receiveMessage);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PONG) {
try {
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by PONG FrameType : " + packet);
webSocket.onPong((byte[]) packet.receiveMessage);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
}
} else if (packet.type == FrameType.CLOSE) {
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
closeRunner(0, "received CLOSE frame-type message");
return;
} else {
context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
closeRunner(0, "received unknown frame-type message");
return;
}
} }
} }
@@ -220,15 +227,16 @@ class WebSocketRunner implements Runnable {
boolean debug = context.getLogger().isLoggable(Level.FINEST); boolean debug = context.getLogger().isLoggable(Level.FINEST);
//System.out.println("推送消息"); //System.out.println("推送消息");
final CompletableFuture<Integer> futureResult = new CompletableFuture<>(); final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
synchronized (writing) {
if (writing.getAndSet(true)) {
queue.add(new QueueEntry(futureResult, packet));
return futureResult;
}
}
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, "sending websocket message: " + packet);
try { try {
synchronized (writing) {
if (writing.getAndSet(true)) {
writeQueue.add(new QueueEntry(futureResult, packet));
return futureResult;
}
}
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(); this.lastSendTime = System.currentTimeMillis();
channel.write(buffers, buffers, new CompletionHandler<Integer, ByteBuffer[]>() { channel.write(buffers, buffers, new CompletionHandler<Integer, ByteBuffer[]>() {
@@ -256,7 +264,7 @@ class WebSocketRunner implements Runnable {
break; break;
} }
} }
if (index >= 0) { if (index >= 0) { //ByteBuffer[]统一回收的可以采用此写法
channel.write(attachments, index, attachments.length - index, attachments, this); channel.write(attachments, index, attachments.length - index, attachments, this);
return; return;
} }
@@ -271,37 +279,40 @@ class WebSocketRunner implements Runnable {
} }
QueueEntry entry = null; QueueEntry entry = null;
synchronized (writing) { synchronized (writing) {
entry = queue.poll(); entry = writeQueue.poll();
if (entry == null) writing.set(false); if (entry == null) writing.set(false);
} }
if (entry != null) { if (entry != null) {
future = entry.future; future = entry.future;
ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), webSocket._engine.cryptor); ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), webSocket._engine.cryptor);
lastSendTime = System.currentTimeMillis(); 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); channel.write(buffers, buffers, this);
} }
} catch (Exception e) { } 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"); 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 @Override
public void failed(Throwable exc, ByteBuffer[] attachments) { public void failed(Throwable exc, ByteBuffer[] attachments) {
writing.set(false); writing.set(false);
future.complete(RETCODE_SENDEXCEPTION);
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler");
if (exc != null) { 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); 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) { } catch (Exception t) {
writing.set(false); 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); 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; return futureResult;
} }
@@ -315,14 +326,16 @@ class WebSocketRunner implements Runnable {
synchronized (this) { synchronized (this) {
if (closed) return; if (closed) return;
closed = true; closed = true;
try { channel.dispose();
channel.close();
} catch (Throwable t) {
}
context.offerBuffer(readBuffer); context.offerBuffer(readBuffer);
readBuffer = null; readBuffer = null;
engine.remove(webSocket); engine.removeThenClose(webSocket);
webSocket.onClose(code, reason); webSocket.onClose(code, reason);
QueueEntry entry = writeQueue.poll();
while (entry != null) {
entry.future.complete(RETCODE_WSOCKET_CLOSED);
entry = writeQueue.poll();
}
} }
} }

View File

@@ -13,7 +13,7 @@ import java.nio.channels.CompletionHandler;
import java.security.*; import java.security.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*; import javax.annotation.*;
import org.redkale.convert.Convert; import org.redkale.convert.Convert;
@@ -50,6 +50,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
@Comment("WebScoket服务器最大连接数为0表示无限制") @Comment("WebScoket服务器最大连接数为0表示无限制")
public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns"; public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns";
@Comment("WebScoket服务器操作WebSocketNode对应CacheSource并发数, 为-1表示无限制为0表示系统默认值(CPU*8)")
public static final String WEBPARAM__WSTHREADS = "wsthreads";
@Comment("最大消息体长度, 小于1表示无限制") @Comment("最大消息体长度, 小于1表示无限制")
public static final String WEBPARAM__WSMAXBODY = "wsmaxbody"; public static final String WEBPARAM__WSMAXBODY = "wsmaxbody";
@@ -76,6 +79,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
//同RestWebSocket.wsmaxconns //同RestWebSocket.wsmaxconns
protected int wsmaxconns = 0; protected int wsmaxconns = 0;
//同RestWebSocket.wsthreads
protected int wsthreads = 0;
//同RestWebSocket.wsmaxbody //同RestWebSocket.wsmaxbody
protected int wsmaxbody = 32 * 1024; protected int wsmaxbody = 32 * 1024;
@@ -147,7 +153,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
} }
//存在WebSocketServlet则此WebSocketNode必须是本地模式Service //存在WebSocketServlet则此WebSocketNode必须是本地模式Service
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
this.single, context, liveinterval, wsmaxconns, wsmaxbody, this.cryptor, 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.init(conf);
this.node.localEngine.init(conf); this.node.localEngine.init(conf);
@@ -241,12 +247,37 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
webSocket._userid = userid; webSocket._userid = userid;
if (single && !anyuser) { if (single && !anyuser) {
WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, nex) -> { WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, nex) -> {
if (rs) webSocket.onSingleRepeatConnect(); if (rs) {
WebSocketServlet.this.node.localEngine.add(webSocket); CompletableFuture<Boolean> rcFuture = webSocket.onSingleRepeatConnect();
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel()); Consumer<Boolean> task = (oldkilled) -> {
webSocket._runner = runner; if (oldkilled) {
context.runAsync(runner); WebSocketServlet.this.node.localEngine.add(webSocket);
response.finish(true); 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 { } else {
WebSocketServlet.this.node.localEngine.add(webSocket); WebSocketServlet.this.node.localEngine.add(webSocket);

View File

@@ -296,7 +296,7 @@ public final class SncpClient {
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null;
final BsonReader reader = bsonConvert.pollBsonReader(); final BsonReader reader = bsonConvert.pollBsonReader();
CompletableFuture<byte[]> future = remote0(handlerFunc, remoteGroupTransport, null, action, params); CompletableFuture<byte[]> future = remote0(handlerFunc, remoteGroupTransport, null, action, params);
if (action.boolReturnTypeFuture) { if (action.boolReturnTypeFuture) { //与handlerFuncIndex互斥
CompletableFuture result = action.futureCreator.create(); CompletableFuture result = action.futureCreator.create();
future.whenComplete((v, e) -> { future.whenComplete((v, e) -> {
try { try {
@@ -355,7 +355,12 @@ public final class SncpClient {
CompletableFuture<AsyncConnection> connFuture = transport.pollConnection(addr); CompletableFuture<AsyncConnection> connFuture = transport.pollConnection(addr);
return connFuture.thenCompose(conn0 -> { return connFuture.thenCompose(conn0 -> {
final CompletableFuture<byte[]> future = new CompletableFuture(); 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")); future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
return future; return future;
} }
@@ -377,7 +382,7 @@ public final class SncpClient {
transport.offerBuffer(attachments[i]); transport.offerBuffer(attachments[i]);
} }
} }
if (index == 0) { if (index == 0) { //ByteBuffer[]不统一回收的必须采用此写法分开
conn.write(attachments, attachments, this); conn.write(attachments, attachments, this);
return; return;
} else if (index > 0) { } else if (index > 0) {
@@ -396,48 +401,58 @@ public final class SncpClient {
@Override @Override
public void completed(Integer count, Void attachment2) { public void completed(Integer count, Void attachment2) {
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读 try {
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data")); if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
transport.offerBuffer(buffer); future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
transport.offerConnection(true, conn); transport.offerBuffer(buffer);
return; transport.offerConnection(true, conn);
} return;
if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全 }
conn.read(buffer, attachment2, this); if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全
return; conn.read(buffer, attachment2, this);
} return;
buffer.flip(); }
if (received > 0) { buffer.flip();
int offset = this.received; if (received > 0) {
this.received += buffer.remaining(); int offset = this.received;
buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset)); this.received += buffer.remaining();
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取 buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset));
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取
buffer.clear();
conn.read(buffer, attachment2, this);
} else {
success();
}
return;
}
checkResult(seqid, action, buffer);
final int respBodyLength = buffer.getInt();
final int retcode = buffer.getInt();
if (retcode != 0) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
}
if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取
this.body = new byte[respBodyLength];
this.received = buffer.remaining();
buffer.get(body, 0, this.received);
buffer.clear(); buffer.clear();
conn.read(buffer, attachment2, this); conn.read(buffer, attachment2, this);
} else { } else {
this.body = new byte[respBodyLength];
buffer.get(body, 0, respBodyLength);
success(); success();
} }
return; } catch (Throwable e) {
} future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote response error"));
checkResult(seqid, action, buffer); transport.offerConnection(true, conn);
if (handler != null) {
final int respBodyLength = buffer.getInt(); final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
final int retcode = buffer.getInt(); handler.failed(e, handlerAttach);
if (retcode != 0) { }
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")"); logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error", e);
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
}
if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取
this.body = new byte[respBodyLength];
this.received = buffer.remaining();
buffer.get(body, 0, this.received);
buffer.clear();
conn.read(buffer, attachment2, this);
} else {
this.body = new byte[respBodyLength];
buffer.get(body, 0, respBodyLength);
success();
} }
} }
@@ -468,7 +483,6 @@ public final class SncpClient {
@Override @Override
public void failed(Throwable exc, Void attachment2) { 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")); future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
transport.offerBuffer(buffer); transport.offerBuffer(buffer);
transport.offerConnection(true, conn); transport.offerConnection(true, conn);
@@ -476,15 +490,21 @@ public final class SncpClient {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
handler.failed(exc, handlerAttach); handler.failed(exc, handlerAttach);
} }
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
} }
}); });
} }
@Override @Override
public void failed(Throwable exc, ByteBuffer[] attachment) { 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.offerBuffer(buffer);
transport.offerConnection(true, conn); 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; return future;

View File

@@ -5,14 +5,9 @@
*/ */
package org.redkale.net.sncp; package org.redkale.net.sncp;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.util.function.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.*;
/** /**
* <p> * <p>
@@ -22,11 +17,26 @@ import org.redkale.util.*;
*/ */
public class SncpContext extends Context { public class SncpContext extends Context {
public SncpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext, public SncpContext(SncpContextConfig config) {
int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, super(config);
int maxbody, Charset charset, InetSocketAddress address, ResourceFactory resourceFactory, }
PrepareServlet prepare, int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, sslContext, bufferCapacity, bufferPool, responsePool, @Override
maxbody, charset, address, resourceFactory, prepare, readTimeoutSecond, writeTimeoutSecond); 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 {
} }
} }

View File

@@ -149,7 +149,7 @@ public final class SncpDynServlet extends SncpServlet {
Object[] sncpParams = handler.sncp_getParams(); Object[] sncpParams = handler.sncp_getParams();
future.whenComplete((v, e) -> { future.whenComplete((v, e) -> {
if (e != null) { 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); response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
return; return;
} }
@@ -162,7 +162,7 @@ public final class SncpDynServlet extends SncpServlet {
} }
} }
} catch (Throwable t) { } 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); response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
} }
} }

View File

@@ -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"); 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) { public void finish(final int retcode, final BsonWriter out) {
if (out == null) { if (out == null) {
final ByteBuffer buffer = pollWriteReadBuffer(); final ByteBuffer buffer = pollWriteReadBuffer();

View File

@@ -10,6 +10,7 @@ import java.util.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import org.redkale.convert.bson.*; import org.redkale.convert.bson.*;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.sncp.SncpContext.SncpContextConfig;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
@@ -106,8 +107,26 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
AtomicLong createResponseCounter = new AtomicLong(); AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong(); AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); 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))); responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext)));
return sncpcontext; return sncpcontext;
} }

View File

@@ -6,8 +6,9 @@
package org.redkale.service; package org.redkale.service;
import java.lang.annotation.*; import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
/** /**
@@ -23,61 +24,28 @@ import java.util.*;
*/ */
@Inherited @Inherited
@Documented @Documented
@Target(value = {ElementType.FIELD}) @Target({FIELD})
@Retention(value = RetentionPolicy.RUNTIME) @Retention(RUNTIME)
@Repeatable(RetLabel.RetLabels.class)
public @interface RetLabel { public @interface RetLabel {
String value(); 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; RetLabel[] value();
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, "未知错误");
}
} }
public static abstract class RetLoader { public static abstract class RetLoader {
public static Map<Integer, String> load(Class clazz) { public static Map<String, Map<Integer, String>> loadMap(Class clazz) {
final Map<Integer, String> rets = new HashMap<>(); final Map<String, Map<Integer, String>> rets = new HashMap<>();
for (Field field : clazz.getFields()) { for (Field field : clazz.getFields()) {
if (!Modifier.isStatic(field.getModifiers())) continue; if (!Modifier.isStatic(field.getModifiers())) continue;
if (field.getType() != int.class) continue; if (field.getType() != int.class) continue;
@@ -90,9 +58,14 @@ public @interface RetLabel {
ex.printStackTrace(); ex.printStackTrace();
continue; continue;
} }
rets.put(value, info.value()); rets.computeIfAbsent(info.locale(), (k) -> new HashMap<>()).put(value, info.value());
} }
return rets; return rets;
} }
@Deprecated
public static Map<Integer, String> load(Class clazz) {
return loadMap(clazz).computeIfAbsent("", (k) -> new HashMap<>());
}
} }
} }

View File

@@ -77,9 +77,10 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
*/ */
@Override @Override
public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) { public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
tryAcquireSemaphore();
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr); 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)); 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); if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
return future; return future;
} }
@@ -94,8 +95,9 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
*/ */
@Override @Override
public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) { public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) {
tryAcquireSemaphore();
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr); 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); if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
return future; return future;
} }
@@ -111,8 +113,10 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
*/ */
@Override @Override
public CompletableFuture<Void> changeUserid(Serializable olduserid, Serializable newuserid, InetSocketAddress sncpAddr) { public CompletableFuture<Void> changeUserid(Serializable olduserid, Serializable newuserid, InetSocketAddress sncpAddr) {
tryAcquireSemaphore();
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, sncpAddr); CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, sncpAddr);
future = future.thenAccept((a) -> sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + olduserid, 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); if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + sncpAddr);
return future; return future;
} }

View File

@@ -10,7 +10,7 @@ import java.lang.reflect.Type;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
@@ -63,6 +63,10 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
protected final ConcurrentHashMap<String, CacheEntry<Object>> container = new ConcurrentHashMap<>(); 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 @RpcRemote
protected CacheSource<V> remoteSource; protected CacheSource<V> remoteSource;
@@ -119,16 +123,20 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}); });
final List<String> keys = new ArrayList<>(); final List<String> keys = new ArrayList<>();
scheduler.scheduleWithFixedDelay(() -> { scheduler.scheduleWithFixedDelay(() -> {
keys.clear(); try {
int now = (int) (System.currentTimeMillis() / 1000); keys.clear();
container.forEach((k, x) -> { int now = (int) (System.currentTimeMillis() / 1000);
if (x.expireSeconds > 0 && (now > (x.lastAccessed + x.expireSeconds))) { container.forEach((k, x) -> {
keys.add(x.key); if (x.expireSeconds > 0 && (now > (x.lastAccessed + x.expireSeconds))) {
keys.add(x.key);
}
});
for (String key : keys) {
CacheEntry entry = container.remove(key);
if (expireHandler != null && entry != null) expireHandler.accept(entry);
} }
}); } catch (Throwable t) {
for (String key : keys) { logger.log(Level.SEVERE, "CacheMemorySource schedule(interval=" + 10 + "s) error", t);
CacheEntry entry = container.remove(key);
if (expireHandler != null && entry != null) expireHandler.accept(entry);
} }
}, 10, 10, TimeUnit.SECONDS); }, 10, 10, TimeUnit.SECONDS);
if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor"); if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
@@ -432,7 +440,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds) { 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) { protected void set(CacheEntryType cacheType, String key, Object value) {
@@ -469,19 +477,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setAsync(String key, V value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setStringAsync(String key, String value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setLongAsync(String key, long value) { 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) { protected void set(CacheEntryType cacheType, int expireSeconds, String key, Object value) {
@@ -518,19 +526,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setAsync(int expireSeconds, String key, V value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setStringAsync(int expireSeconds, String key, String value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setLongAsync(int expireSeconds, String key, long value) { 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 @Override
@@ -545,7 +553,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds) { 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 @Override
@@ -564,7 +572,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Long> incrAsync(final String key) { public CompletableFuture<Long> incrAsync(final String key) {
return CompletableFuture.supplyAsync(() -> incr(key), getExecutor()); return CompletableFuture.supplyAsync(() -> incr(key), getExecutor()).whenComplete(futureCompleteConsumer);
} }
@Override @Override
@@ -586,7 +594,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Long> incrAsync(final String key, long num) { 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 @Override
@@ -598,7 +606,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Long> decrAsync(final String key) { public CompletableFuture<Long> decrAsync(final String key) {
return CompletableFuture.supplyAsync(() -> decr(key), getExecutor()); return CompletableFuture.supplyAsync(() -> decr(key), getExecutor()).whenComplete(futureCompleteConsumer);
} }
@Override @Override
@@ -610,13 +618,13 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Long> decrAsync(final String key, long num) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeAsync(final String key) { public CompletableFuture<Void> removeAsync(final String key) {
return CompletableFuture.runAsync(() -> remove(key), getExecutor()); return CompletableFuture.runAsync(() -> remove(key), getExecutor()).whenComplete(futureCompleteConsumer);
} }
@Override @Override
@@ -764,19 +772,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> appendListItemAsync(final String key, final V value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> appendLongListItemAsync(final String key, final long value) { 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 @Override
@@ -809,19 +817,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeListItemAsync(final String key, final V value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeLongListItemAsync(final String key, final long value) { 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) { protected void appendSetItem(CacheEntryType cacheType, String key, Object value) {
@@ -859,19 +867,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> appendLongSetItemAsync(final String key, final long value) { 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 @Override
@@ -904,19 +912,19 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
@Override @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value) { 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 @Override
@RpcMultiRun @RpcMultiRun
public CompletableFuture<Void> removeLongSetItemAsync(final String key, final long value) { 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 @Override
@@ -924,6 +932,22 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
return new ArrayList<>(container.keySet()); 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 @Override
public int getKeySize() { public int getKeySize() {
return container.size(); return container.size();
@@ -944,6 +968,16 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
return CompletableFuture.completedFuture(new ArrayList<>(container.keySet())); 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 @Override
public CompletableFuture<Integer> getKeySizeAsync() { public CompletableFuture<Integer> getKeySizeAsync() {
return CompletableFuture.completedFuture(container.size()); return CompletableFuture.completedFuture(container.size());

View File

@@ -98,6 +98,10 @@ public interface CacheSource<V extends Object> {
public List<String> queryKeys(); public List<String> queryKeys();
public List<String> queryKeysStartsWith(String startsWith);
public List<String> queryKeysEndsWith(String endsWith);
public int getKeySize(); public int getKeySize();
public List<CacheEntry<Object>> queryList(); public List<CacheEntry<Object>> queryList();
@@ -215,6 +219,10 @@ public interface CacheSource<V extends Object> {
public CompletableFuture<List<String>> queryKeysAsync(); 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<Integer> getKeySizeAsync();
public CompletableFuture<List<CacheEntry< Object>>> queryListAsync(); public CompletableFuture<List<CacheEntry< Object>>> queryListAsync();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -33,26 +33,28 @@ public interface DataSource {
//----------------------insertAsync----------------------------- //----------------------insertAsync-----------------------------
/** /**
* 新增记录, 多对象必须是同一个Entity类 <br> * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
* *
* @param <T> 泛型 * @param <T> 泛型
* @param values Entity对象 * @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 <T> 泛型
* @param values Entity对象 * @param values Entity对象
* *
* @return CompletableFuture * @return CompletableFuture
*/ */
public <T> CompletableFuture<Void> insertAsync(final T... values); public <T> CompletableFuture<Integer> insertAsync(final T... values);
//-------------------------deleteAsync-------------------------- //-------------------------deleteAsync--------------------------
/** /**
* 删除指定主键值的记录, 多对象必须是同一个Entity类 <br> * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br> * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br>
* *
* @param <T> 泛型 * @param <T> 泛型
@@ -63,7 +65,7 @@ public interface DataSource {
public <T> int delete(final T... values); public <T> int delete(final T... values);
/** /**
* 删除指定主键值的记录, 多对象必须是同一个Entity类 <br> * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br> * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br>
* *
* @param <T> 泛型 * @param <T> 泛型
@@ -74,7 +76,7 @@ public interface DataSource {
public <T> CompletableFuture<Integer> deleteAsync(final T... values); public <T> CompletableFuture<Integer> deleteAsync(final T... values);
/** /**
* 删除指定主键值的记录 <br> * 删除指定主键值的记录,多主键值必须在同一张表中 <br>
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br> * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br>
* *
* @param <T> Entity泛型 * @param <T> Entity泛型
@@ -86,7 +88,7 @@ public interface DataSource {
public <T> int delete(final Class<T> clazz, final Serializable... ids); public <T> int delete(final Class<T> clazz, final Serializable... ids);
/** /**
* 删除指定主键值的记录 <br> * 删除指定主键值的记录,多主键值必须在同一张表中 <br>
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br> * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br>
* *
* @param <T> Entity泛型 * @param <T> Entity泛型
@@ -151,7 +153,7 @@ public interface DataSource {
//------------------------updateAsync--------------------------- //------------------------updateAsync---------------------------
/** /**
* 更新记录, 多对象必须是同一个Entity类 <br> * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
* 等价SQL: <br> * 等价SQL: <br>
* UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id1} <br> * UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id1} <br>
* UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id2} <br> * UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id2} <br>
@@ -165,7 +167,7 @@ public interface DataSource {
public <T> int update(final T... values); public <T> int update(final T... values);
/** /**
* 更新记录, 多对象必须是同一个Entity类 <br> * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中 <br>
* 等价SQL: <br> * 等价SQL: <br>
* UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id1} <br> * UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id1} <br>
* UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id2} <br> * UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id2} <br>

View File

@@ -25,6 +25,8 @@ public final class DataSources {
public static final String JDBC_CONNECTIONSMAX = "javax.persistence.connections.limit"; 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_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate";
public static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.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_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_URL = "javax.persistence.jdbc.url";
public static final String JDBC_USER = "javax.persistence.jdbc.user"; 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 { 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 { 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 { 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()); : new File(System.getProperty(DATASOURCE_CONFPATH)).toURI().toURL());
} }
public static DataSource createDataSource(final String unitName, URL url) throws IOException { public static DataSource createDataSource(final String unitName, URL persistxml) throws IOException {
if (url == null) url = DataSources.class.getResource("/persistence.xml"); if (persistxml == null) persistxml = DataSources.class.getResource("/persistence.xml");
InputStream in = url.openStream(); InputStream in = persistxml.openStream();
if (in == null) return null; if (in == null) return null;
Map<String, Properties> map = loadPersistenceXml(in); Map<String, Properties> map = loadPersistenceXml(in);
Properties readprop = null; Properties readprop = null;
@@ -94,7 +102,40 @@ public final class DataSources {
if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource"); if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource");
if (writeprop == null) writeprop = readprop; if (writeprop == null) writeprop = readprop;
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName()); 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 { try {
Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl); Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl);
for (Constructor d : ds.getConstructors()) { for (Constructor d : ds.getConstructors()) {
@@ -105,9 +146,11 @@ public final class DataSources {
return (DataSource) d.newInstance(unitName, readprop); return (DataSource) d.newInstance(unitName, readprop);
} else if (paramtypes.length == 3 && paramtypes[0] == String.class && paramtypes[1] == Properties.class && paramtypes[2] == Properties.class) { } else if (paramtypes.length == 3 && paramtypes[0] == String.class && paramtypes[1] == Properties.class && paramtypes[2] == Properties.class) {
return (DataSource) d.newInstance(unitName, readprop, writeprop); 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) { } catch (IOException ex) {
throw ex; throw ex;
} catch (Exception e) { } catch (Exception e) {

File diff suppressed because it is too large Load Diff

View File

@@ -118,15 +118,19 @@ public final class EntityCache<T> {
return t; return t;
}); });
this.scheduler.scheduleAtFixedRate(() -> { this.scheduler.scheduleAtFixedRate(() -> {
ConcurrentHashMap newmap2 = new ConcurrentHashMap(); try {
List<T> all2 = info.fullloader.apply(info.source, type); ConcurrentHashMap newmap2 = new ConcurrentHashMap();
if (all2 != null) { List<T> all2 = info.fullloader.apply(info.source, type);
all2.stream().filter(x -> x != null).forEach(x -> { if (all2 != null) {
newmap2.put(this.primary.get(x), x); all2.stream().filter(x -> x != null).forEach(x -> {
}); newmap2.put(this.primary.get(x), x);
});
}
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);
} }
this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2);
this.map = newmap2;
}, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS); }, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS);
} }
} }

View File

@@ -91,25 +91,49 @@ public final class EntityInfo<T> {
final DistributeTableStrategy<T> tableStrategy; final DistributeTableStrategy<T> tableStrategy;
//根据主键查找单个对象的SQL //根据主键查找单个对象的SQL
final String querySQL; private final String queryPrepareSQL;
//根据主键查找单个对象的SQL 含 $
private final String queryDollarPrepareSQL;
//根据主键查找单个对象的SQL 含 :name
private final String queryNamesPrepareSQL;
//数据库中所有字段 //数据库中所有字段
private final Attribute<T, Serializable>[] queryAttributes; private final Attribute<T, Serializable>[] queryAttributes;
//新增SQL 含 ?,即排除了自增长主键和标记为&#064;Column(insertable=false)的字段 //新增SQL 含 ?,即排除了自增长主键和标记为&#064;Column(insertable=false)的字段
private final String insertSQL; private final String insertPrepareSQL;
//新增SQL 含 $,即排除了自增长主键和标记为&#064;Column(insertable=false)的字段
private final String insertDollarPrepareSQL;
//新增SQL 含 :name即排除了自增长主键和标记为&#064;Column(insertable=false)的字段
private final String insertNamesPrepareSQL;
//数据库中所有可新增字段 //数据库中所有可新增字段
final Attribute<T, Serializable>[] insertAttributes; final Attribute<T, Serializable>[] insertAttributes;
//根据主键更新所有可更新字段的SQL //根据主键更新所有可更新字段的SQL
private final String updateSQL; private final String updatePrepareSQL;
//根据主键更新所有可更新字段的SQL含 $
private final String updateDollarPrepareSQL;
//根据主键更新所有可更新字段的SQL含 :name
private final String updateNamesPrepareSQL;
//数据库中所有可更新字段 //数据库中所有可更新字段
final Attribute<T, Serializable>[] updateAttributes; final Attribute<T, Serializable>[] updateAttributes;
//根据主键删除记录的SQL //根据主键删除记录的SQL
private final String deleteSQL; private final String deletePrepareSQL;
//根据主键删除记录的SQL含 $
private final String deleteDollarPrepareSQL;
//根据主键删除记录的SQL含 :name
private final String deleteNamesPrepareSQL;
//日志级别从LogLevel获取 //日志级别从LogLevel获取
private final int logLevel; private final int logLevel;
@@ -133,7 +157,7 @@ public final class EntityInfo<T> {
/** /**
* 加载EntityInfo * 加载EntityInfo
* *
* @param type Entity类 * @param clazz Entity类
* @param cacheForbidden 是否禁用EntityCache * @param cacheForbidden 是否禁用EntityCache
* @param conf 配置信息, persistence.xml中的property节点值 * @param conf 配置信息, persistence.xml中的property节点值
* @param source DataSource,可为null * @param source DataSource,可为null
@@ -312,27 +336,63 @@ public final class EntityInfo<T> {
} }
if (table != null) { if (table != null) {
StringBuilder insertsb = new StringBuilder(); StringBuilder insertsb = new StringBuilder();
StringBuilder insertsb2 = new StringBuilder(); StringBuilder insertsbjdbc = new StringBuilder();
StringBuilder insertsbdollar = new StringBuilder();
StringBuilder insertsbnames = new StringBuilder();
int index = 0;
for (String col : insertcols) { for (String col : insertcols) {
if (insertsb.length() > 0) insertsb.append(','); if (index > 0) insertsb.append(',');
insertsb.append(col); insertsb.append(col);
if (insertsb2.length() > 0) insertsb2.append(','); if (index > 0) {
insertsb2.append('?'); insertsbjdbc.append(',');
insertsbdollar.append(',');
insertsbnames.append(',');
}
insertsbjdbc.append('?');
insertsbdollar.append("$").append(++index);
insertsbnames.append(":").append(col);
} }
this.insertSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsb2 + ")"; this.insertPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbjdbc + ")";
this.insertDollarPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbdollar + ")";
this.insertNamesPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbnames + ")";
StringBuilder updatesb = new StringBuilder(); StringBuilder updatesb = new StringBuilder();
StringBuilder updatesbdollar = new StringBuilder();
StringBuilder updatesbnames = new StringBuilder();
index = 0;
for (String col : updatecols) { for (String col : updatecols) {
if (updatesb.length() > 0) updatesb.append(", "); if (index > 0) {
updatesb.append(", ");
updatesbdollar.append(", ");
updatesbnames.append(", ");
}
updatesb.append(col).append(" = ?"); updatesb.append(col).append(" = ?");
updatesbdollar.append(col).append(" = ").append("$").append(++index);
updatesbnames.append(col).append(" = :").append(col);
} }
this.updateSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesb + " WHERE " + getPrimarySQLColumn(null) + " = ?"; this.updatePrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesb + " WHERE " + getPrimarySQLColumn(null) + " = ?";
this.deleteSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = ?"; this.updateDollarPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbdollar + " WHERE " + getPrimarySQLColumn(null) + " = $" + (++index);
this.querySQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = ?"; this.updateNamesPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbnames + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null);
this.deletePrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = ?";
this.deleteDollarPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = $1";
this.deleteNamesPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null);
this.queryPrepareSQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = ?";
this.queryDollarPrepareSQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = $1";
this.queryNamesPrepareSQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null);
} else { } else {
this.insertSQL = null; this.insertPrepareSQL = null;
this.updateSQL = null; this.updatePrepareSQL = null;
this.deleteSQL = null; this.deletePrepareSQL = null;
this.querySQL = null; this.queryPrepareSQL = null;
this.insertDollarPrepareSQL = null;
this.updateDollarPrepareSQL = null;
this.deleteDollarPrepareSQL = null;
this.queryDollarPrepareSQL = null;
this.insertNamesPrepareSQL = null;
this.updateNamesPrepareSQL = null;
this.deleteNamesPrepareSQL = null;
this.queryNamesPrepareSQL = null;
} }
this.autoGenerated = auto; this.autoGenerated = auto;
this.autouuid = uuid; this.autouuid = uuid;
@@ -405,6 +465,79 @@ public final class EntityInfo<T> {
return table == null; return table == null;
} }
public boolean isAutoGenerated() {
return autoGenerated;
}
public boolean isAutouuid() {
return autouuid;
}
public DistributeTableStrategy<T> getTableStrategy() {
return tableStrategy;
}
public String getTableNotExistSqlStates2() {
return tablenotexistSqlstates;
}
public boolean isTableNotExist(String code) {
return tablenotexistSqlstates.contains(';' + code + ';');
}
public boolean isTableNotExist(SQLException e) {
if (e == null) return false;
return tablenotexistSqlstates.contains(';' + e.getSQLState() + ';');
}
public Attribute<T, Serializable>[] getInsertAttributes() {
return insertAttributes;
}
public Attribute<T, Serializable>[] getUpdateAttributes() {
return updateAttributes;
}
public Attribute<T, Serializable>[] getQueryAttributes() {
return queryAttributes;
}
/**
* 获取Entity的QUERY SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getQueryPrepareSQL(T bean) {
if (this.tableStrategy == null) return queryPrepareSQL;
return queryPrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的QUERY SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getQueryDollarPrepareSQL(T bean) {
if (this.tableStrategy == null) return queryDollarPrepareSQL;
return queryDollarPrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的QUERY SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getQueryNamesPrepareSQL(T bean) {
if (this.tableStrategy == null) return queryNamesPrepareSQL;
return queryNamesPrepareSQL.replace("${newtable}", getTable(bean));
}
/** /**
* 获取Entity的INSERT SQL * 获取Entity的INSERT SQL
* *
@@ -412,9 +545,33 @@ public final class EntityInfo<T> {
* *
* @return String * @return String
*/ */
public String getInsertSQL(T bean) { public String getInsertPrepareSQL(T bean) {
if (this.tableStrategy == null) return insertSQL; if (this.tableStrategy == null) return insertPrepareSQL;
return insertSQL.replace("${newtable}", getTable(bean)); return insertPrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的INSERT SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getInsertDollarPrepareSQL(T bean) {
if (this.tableStrategy == null) return insertDollarPrepareSQL;
return insertDollarPrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的INSERT SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getInsertNamesPrepareSQL(T bean) {
if (this.tableStrategy == null) return insertNamesPrepareSQL;
return insertNamesPrepareSQL.replace("${newtable}", getTable(bean));
} }
/** /**
@@ -424,9 +581,33 @@ public final class EntityInfo<T> {
* *
* @return String * @return String
*/ */
public String getUpdateSQL(T bean) { public String getUpdatePrepareSQL(T bean) {
if (this.tableStrategy == null) return updateSQL; if (this.tableStrategy == null) return updatePrepareSQL;
return updateSQL.replace("${newtable}", getTable(bean)); return updatePrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的UPDATE SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getUpdateDollarPrepareSQL(T bean) {
if (this.tableStrategy == null) return updateDollarPrepareSQL;
return updateDollarPrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的UPDATE SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getUpdateNamesPrepareSQL(T bean) {
if (this.tableStrategy == null) return updateNamesPrepareSQL;
return updateNamesPrepareSQL.replace("${newtable}", getTable(bean));
} }
/** /**
@@ -436,9 +617,33 @@ public final class EntityInfo<T> {
* *
* @return String * @return String
*/ */
public String getDeleteSQL(T bean) { public String getDeletePrepareSQL(T bean) {
if (this.tableStrategy == null) return deleteSQL; if (this.tableStrategy == null) return deletePrepareSQL;
return deleteSQL.replace("${newtable}", getTable(bean)); return deletePrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的DELETE SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getDeleteDollarPrepareSQL(T bean) {
if (this.tableStrategy == null) return deleteDollarPrepareSQL;
return deleteDollarPrepareSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取Entity的DELETE SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getDeleteNamesPrepareSQL(T bean) {
if (this.tableStrategy == null) return deleteNamesPrepareSQL;
return deleteNamesPrepareSQL.replace("${newtable}", getTable(bean));
} }
/** /**
@@ -461,6 +666,10 @@ public final class EntityInfo<T> {
return sb; return sb;
} }
public String getTableCopySQL(String newTable) {
return tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", table);
}
/** /**
* 根据主键值获取Entity的表名 * 根据主键值获取Entity的表名
* *

View File

@@ -40,11 +40,11 @@ public class FilterFuncColumn implements java.io.Serializable {
return new FilterFuncColumn(func, defvalue, columns); return new FilterFuncColumn(func, defvalue, columns);
} }
String[] cols() { public String[] cols() {
return columns == null || columns.length == 0 ? new String[]{COLUMN_NULL} : columns; return columns == null || columns.length == 0 ? new String[]{COLUMN_NULL} : columns;
} }
String col(String column) { public String col(String column) {
return column == null || column.isEmpty() ? COLUMN_NULL : column; return column == null || column.isEmpty() ? COLUMN_NULL : column;
} }

View File

@@ -8,13 +8,13 @@ package org.redkale.source;
import java.io.*; import java.io.*;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.*; import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.logging.*;
import java.util.logging.Level;
import javax.sql.*; import javax.sql.*;
import static org.redkale.source.DataSources.*; import static org.redkale.source.DataSources.*;
@@ -25,86 +25,58 @@ import static org.redkale.source.DataSources.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public class PoolJdbcSource { public class PoolJdbcSource extends PoolSource<Connection> {
private static final Map<String, AbstractMap.SimpleEntry<WatchService, List<WeakReference<PoolJdbcSource>>>> maps = new HashMap<>(); private static final Map<String, AbstractMap.SimpleEntry<WatchService, List<WeakReference<PoolJdbcSource>>>> maps = new HashMap<>();
private final AtomicLong usingCounter = new AtomicLong();
private final AtomicLong creatCounter = new AtomicLong();
private final AtomicLong cycleCounter = new AtomicLong();
private final AtomicLong saveCounter = new AtomicLong();
private final ConnectionPoolDataSource source; private final ConnectionPoolDataSource source;
private final ArrayBlockingQueue<PooledConnection> queue; private final ArrayBlockingQueue<PooledConnection> queue;
private final ConnectionEventListener listener; private final ConnectionEventListener listener;
private final DataJdbcSource dataSource; private final String unitName;
private final String stype; // "" 或 "read" 或 "write" private final URL persistxml;
private final int maxconns; public PoolJdbcSource(String unitName, URL persistxml, String rwtype, ArrayBlockingQueue aqueue, Properties prop, Logger logger) {
super(rwtype, prop, logger);
private String url; this.unitName = unitName;
this.persistxml = persistxml;
private String user;
private String password;
final Properties props;
public PoolJdbcSource(DataJdbcSource source, String stype, Properties prop) {
this.dataSource = source;
this.stype = stype;
this.props = prop;
this.source = createDataSource(prop); this.source = createDataSource(prop);
this.url = prop.getProperty(JDBC_URL); this.queue = aqueue == null ? new ArrayBlockingQueue<>(this.maxconns) : aqueue;
this.user = prop.getProperty(JDBC_USER);
this.password = prop.getProperty(JDBC_PWD);
this.maxconns = Integer.decode(prop.getProperty(JDBC_CONNECTIONSMAX, "" + Runtime.getRuntime().availableProcessors() * 16));
this.queue = new ArrayBlockingQueue<>(this.maxconns);
this.listener = new ConnectionEventListener() { this.listener = new ConnectionEventListener() {
@Override @Override
public void connectionClosed(ConnectionEvent event) { public void connectionClosed(ConnectionEvent event) {
PooledConnection pc = (PooledConnection) event.getSource(); PooledConnection pc = (PooledConnection) event.getSource();
if (queue.offer(pc)) saveCounter.incrementAndGet(); if (queue.offer(pc)) {
saveCounter.incrementAndGet();
} else {
try {
pc.close();
} catch (Exception e) {
logger.log(Level.INFO, DataSource.class.getSimpleName() + " " + pc + " close error", e);
}
}
} }
@Override @Override
public void connectionErrorOccurred(ConnectionEvent event) { public void connectionErrorOccurred(ConnectionEvent event) {
usingCounter.decrementAndGet(); usingCounter.decrementAndGet();
if ("08S01".equals(event.getSQLException().getSQLState())) return; //MySQL特性 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException if ("08S01".equals(event.getSQLException().getSQLState())) return; //MySQL特性 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
dataSource.logger.log(Level.WARNING, "connectionErronOccurred [" + event.getSQLException().getSQLState() + "]", event.getSQLException()); logger.log(Level.WARNING, "connectionErronOccurred [" + event.getSQLException().getSQLState() + "]", event.getSQLException());
} }
}; };
if (this.isOracle()) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0");
if (!this.props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) {
this.props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02");
}
if (!this.props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) {
//注意:此语句复制表结构会导致默认值和主键信息的丢失
this.props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2");
}
} else if (this.isSqlserver()) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0");
}
try { try {
this.watch(); this.watch();
} catch (Exception e) { } catch (Exception e) {
dataSource.logger.log(Level.WARNING, DataSource.class.getSimpleName() + " watch " + dataSource.conf + " error", e); logger.log(Level.WARNING, DataSource.class.getSimpleName() + " watch " + persistxml + " error", e);
} }
} }
static ConnectionPoolDataSource createDataSource(Properties property) { private static ConnectionPoolDataSource createDataSource(Properties property) {
try { try {
return createDataSource(property.getProperty(JDBC_SOURCE, property.getProperty(JDBC_DRIVER)), return createDataSource(property.getProperty(JDBC_SOURCE, property.getProperty(JDBC_DRIVER)),
property.getProperty(JDBC_URL), property.getProperty(JDBC_USER), property.getProperty(JDBC_PWD)); property.getProperty(JDBC_URL), property.getProperty(JDBC_USER), property.getProperty(JDBC_PWD));
@@ -113,7 +85,7 @@ public class PoolJdbcSource {
} }
} }
static ConnectionPoolDataSource createDataSource(String source0, String url, String user, String password) throws Exception { private static ConnectionPoolDataSource createDataSource(String source0, String url, String user, String password) throws Exception {
String source = source0; String source = source0;
if (source0 == null || source0.isEmpty()) { if (source0 == null || source0.isEmpty()) {
if (url.startsWith("jdbc:mysql:")) { if (url.startsWith("jdbc:mysql:")) {
@@ -175,25 +147,14 @@ public class PoolJdbcSource {
return (ConnectionPoolDataSource) pdsource; return (ConnectionPoolDataSource) pdsource;
} }
final boolean isMysql() { @Override
return source != null && source.getClass().getName().contains(".mysql."); protected int getDefaultPort() {
} return 0;
final boolean isOracle() {
return source != null && source.getClass().getName().contains("oracle.");
}
final boolean isSqlserver() {
return source != null && source.getClass().getName().contains(".sqlserver.");
}
final boolean isPostgresql() {
return source != null && source.getClass().getName().contains(".postgresql.");
} }
private void watch() throws IOException { private void watch() throws IOException {
if (dataSource.conf == null || dataSource.name == null) return; if (persistxml == null || unitName == null) return;
final String file = dataSource.conf.getFile(); final String file = persistxml.getFile();
final File f = new File(file); final File f = new File(file);
if (!f.isFile() || !f.canRead()) return; if (!f.isFile() || !f.canRead()) return;
synchronized (maps) { synchronized (maps) {
@@ -225,18 +186,18 @@ public class PoolJdbcSource {
PoolJdbcSource pool = ref.get(); PoolJdbcSource pool = ref.get();
if (pool == null) continue; if (pool == null) continue;
try { try {
Properties property = m.get(pool.dataSource.name); Properties property = m.get(unitName);
if (property == null) property = m.get(pool.dataSource.name + "." + pool.stype); if (property == null) property = m.get(unitName + "." + pool.rwtype);
if (property != null) pool.change(property); if (property != null) pool.change(property);
} catch (Exception ex) { } catch (Exception ex) {
dataSource.logger.log(Level.INFO, event.context() + " occur error", ex); logger.log(Level.INFO, event.context() + " occur error", ex);
} }
} }
}); });
key.reset(); key.reset();
} }
} catch (Exception e) { } catch (Exception e) {
dataSource.logger.log(Level.WARNING, "DataSource watch " + file + " occur error", e); logger.log(Level.WARNING, "DataSource watch " + file + " occur error", e);
} }
} }
}; };
@@ -244,20 +205,21 @@ public class PoolJdbcSource {
watchThread.setName("DataSource-Watch-" + maps.size() + "-Thread"); watchThread.setName("DataSource-Watch-" + maps.size() + "-Thread");
watchThread.setDaemon(true); watchThread.setDaemon(true);
watchThread.start(); watchThread.start();
dataSource.logger.log(Level.INFO, watchThread.getName() + " start watching " + file); logger.log(Level.INFO, watchThread.getName() + " start watching " + file);
//----------------------------------------------------------- //-----------------------------------------------------------
list.add(new WeakReference<>(this)); list.add(new WeakReference<>(this));
maps.put(file, new AbstractMap.SimpleEntry<>(watcher, list)); maps.put(file, new AbstractMap.SimpleEntry<>(watcher, list));
} }
} }
@Override
public void change(Properties property) { public void change(Properties property) {
Method seturlm; Method seturlm;
Class clazz = source.getClass(); Class clazz = source.getClass();
String newurl = property.getProperty(JDBC_URL); String newurl = property.getProperty(JDBC_URL);
String newuser = property.getProperty(JDBC_USER); String newuser = property.getProperty(JDBC_USER);
String newpassword = property.getProperty(JDBC_PWD); String newpassword = property.getProperty(JDBC_PWD);
if (this.url.equals(newurl) && this.user.equals(newuser) && this.password.equals(newpassword)) return; if (this.url.equals(newurl) && this.username.equals(newuser) && this.password.equals(newpassword)) return;
try { try {
try { try {
seturlm = clazz.getMethod("setUrl", String.class); seturlm = clazz.getMethod("setUrl", String.class);
@@ -268,21 +230,37 @@ public class PoolJdbcSource {
clazz.getMethod("setUser", String.class).invoke(source, newuser); clazz.getMethod("setUser", String.class).invoke(source, newuser);
clazz.getMethod("setPassword", String.class).invoke(source, newpassword); clazz.getMethod("setPassword", String.class).invoke(source, newpassword);
this.url = newurl; this.url = newurl;
this.user = newuser; this.username = newuser;
this.password = newpassword; this.password = newpassword;
dataSource.logger.log(Level.INFO, DataSource.class.getSimpleName() + "(" + dataSource.name + "." + stype + ") change (" + property + ")"); logger.log(Level.INFO, DataSource.class.getSimpleName() + "(" + unitName + "." + rwtype + ") change (" + property + ")");
} catch (Exception e) { } catch (Exception e) {
dataSource.logger.log(Level.SEVERE, DataSource.class.getSimpleName() + " dynamic change JDBC (url userName password) error", e); logger.log(Level.SEVERE, DataSource.class.getSimpleName() + " dynamic change JDBC (url userName password) error", e);
} }
} }
@Override
public void offerConnection(final Connection conn) {
if (conn == null) return;
try {
conn.close();
} catch (Exception e) {
logger.log(Level.WARNING, "closeSQLConnection abort", e);
}
}
@Override
public Connection poll() { public Connection poll() {
return poll(0, null); return poll(0, null);
} }
@Override
public CompletableFuture<Connection> pollAsync() {
return CompletableFuture.completedFuture(poll());
}
private Connection poll(final int count, SQLException e) { private Connection poll(final int count, SQLException e) {
if (count >= 3) { if (count >= 3) {
dataSource.logger.log(Level.WARNING, "create pooled connection error", e); logger.log(Level.WARNING, "create pooled connection error", e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
PooledConnection result = queue.poll(); PooledConnection result = queue.poll();
@@ -291,7 +269,7 @@ public class PoolJdbcSource {
try { try {
result = queue.poll(6, TimeUnit.SECONDS); result = queue.poll(6, TimeUnit.SECONDS);
} catch (Exception t) { } catch (Exception t) {
dataSource.logger.log(Level.WARNING, "take pooled connection error", t); logger.log(Level.WARNING, "take pooled connection error", t);
} }
} }
if (result == null) { if (result == null) {
@@ -311,31 +289,20 @@ public class PoolJdbcSource {
try { try {
conn = result.getConnection(); conn = result.getConnection();
if (!conn.isValid(1)) { if (!conn.isValid(1)) {
dataSource.logger.info("sql connection is not vaild"); logger.info("sql connection is not vaild");
usingCounter.decrementAndGet(); usingCounter.decrementAndGet();
return poll(0, null); return poll(0, null);
} }
} catch (SQLException ex) { } catch (SQLException ex) {
if (!"08S01".equals(ex.getSQLState())) {//MySQL特性 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException if (!"08S01".equals(ex.getSQLState())) {//MySQL特性 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
dataSource.logger.log(Level.FINER, "result.getConnection from pooled connection abort [" + ex.getSQLState() + "]", ex); logger.log(Level.FINER, "result.getConnection from pooled connection abort [" + ex.getSQLState() + "]", ex);
} }
return poll(0, null); return poll(0, null);
} }
return conn; return conn;
} }
public long getCreatCount() { @Override
return creatCounter.longValue();
}
public long getCycleCount() {
return cycleCounter.longValue();
}
public long getSaveCount() {
return saveCounter.longValue();
}
public void close() { public void close() {
queue.stream().forEach(x -> { queue.stream().forEach(x -> {
try { try {
@@ -344,4 +311,5 @@ public class PoolJdbcSource {
} }
}); });
} }
} }

View File

@@ -0,0 +1,207 @@
/*
* 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.source;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import static org.redkale.source.DataSources.*;
/**
* 连接池类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <DBChannel> 连接泛型
*/
public abstract class PoolSource<DBChannel> {
protected final AtomicLong usingCounter = new AtomicLong();
protected final AtomicLong creatCounter = new AtomicLong();
protected final AtomicLong cycleCounter = new AtomicLong();
protected final AtomicLong saveCounter = new AtomicLong();
protected final Logger logger;
protected final String rwtype; // "" 或 "read" 或 "write"
protected final int maxconns;
protected final String dbtype;
protected int connectTimeoutSeconds;
protected int readTimeoutSeconds;
protected int writeTimeoutSeconds;
protected String url;
protected InetSocketAddress servaddr;
protected String username;
protected String password;
protected String database;
protected Properties props;
protected Properties attributes = new Properties();
@SuppressWarnings("OverridableMethodCallInConstructor")
public PoolSource(String rwtype, Properties prop, Logger logger) {
this.logger = logger;
this.rwtype = rwtype;
this.props = prop;
this.url = prop.getProperty(JDBC_URL);
this.username = prop.getProperty(JDBC_USER, "");
this.password = prop.getProperty(JDBC_PWD, "");
this.connectTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_CONNECTTIMEOUT_SECONDS, "3"));
this.readTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_READTIMEOUT_SECONDS, "3"));
this.writeTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_WRITETIMEOUT_SECONDS, "3"));
this.maxconns = Math.max(8, Integer.decode(prop.getProperty(JDBC_CONNECTIONSMAX, "" + Runtime.getRuntime().availableProcessors() * 16)));
String dbtype0 = "";
{ //jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串
int pos = this.url.indexOf("://");
if (pos > 0) {
String url0 = this.url.substring(0, pos);
pos = url0.lastIndexOf(':');
if (pos > 0) dbtype0 = url0.substring(pos + 1);
} else { //jdbc:oracle:thin:@localhost:1521
String url0 = url.substring(url.indexOf(":") + 1);
pos = url0.indexOf(':');
if (pos > 0) dbtype0 = url0.substring(0, pos);
}
}
this.dbtype = dbtype0.toLowerCase();
parseAddressAndDbnameAndAttrs();
if ("oracle".equals(this.dbtype)) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0");
if (!this.props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) {
this.props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02");
}
if (!this.props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) {
//注意:此语句复制表结构会导致默认值和主键信息的丢失
this.props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2");
}
} else if ("sqlserver".equals(this.dbtype)) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0");
} else if ("postgresql".equals(this.dbtype)) {
if (!this.props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) { //注意:此语句复制表结构会导致默认值和主键信息的丢失
//注意postgresql不支持跨库复制表结构
this.props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS (SELECT * FROM ${oldtable} LIMIT 0)");
}
if (!this.props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) {
this.props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42P01;3F000");
}
}
}
protected void parseAddressAndDbnameAndAttrs() {
String url0 = this.url.substring(this.url.indexOf("://") + 3);
int pos = url0.indexOf('?'); //127.0.0.1:5432/db?charset=utr8&xxx=yy
if (pos > 0) {
String params = url0.substring(pos + 1).replace("&amp;", "&");
for (String param : params.split("&")) {
int p = param.indexOf('=');
if (p < 1) continue;
this.attributes.put(param.substring(0, p), param.substring(p + 1));
}
url0 = url0.substring(0, pos);
}
pos = url0.indexOf('/'); //127.0.0.1:5432/db
if (pos > 0) {
this.database = url0.substring(pos + 1);
url0 = url0.substring(0, pos);
}
pos = url0.indexOf(':');
if (pos > 0) {
this.servaddr = new InetSocketAddress(url0.substring(0, pos), Integer.parseInt(url0.substring(pos + 1)));
} else {
this.servaddr = new InetSocketAddress(url0, getDefaultPort());
}
}
protected abstract int getDefaultPort();
public abstract void change(Properties property);
public abstract DBChannel poll();
public abstract CompletableFuture<DBChannel> pollAsync();
public abstract void offerConnection(final DBChannel conn);
public abstract void close();
public final String getDbtype() {
return dbtype;
}
public final long getUsingCount() {
return usingCounter.longValue();
}
public final long getCreatCount() {
return creatCounter.longValue();
}
public final long getCycleCount() {
return cycleCounter.longValue();
}
public final long getSaveCount() {
return saveCounter.longValue();
}
public final int getMaxconns() {
return maxconns;
}
public final int getConnectTimeoutSeconds() {
return connectTimeoutSeconds;
}
public final int getReadTimeoutSeconds() {
return readTimeoutSeconds;
}
public final int getWriteTimeoutSeconds() {
return writeTimeoutSeconds;
}
public final String getUrl() {
return url;
}
public final InetSocketAddress getServaddr() {
return servaddr;
}
public final String getUsername() {
return username;
}
public final String getPassword() {
return password;
}
public final String getDatabase() {
return database;
}
}

View File

@@ -0,0 +1,219 @@
/*
* 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.source;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.sql.*;
import java.util.Properties;
import java.util.concurrent.*;
import java.util.logging.*;
import org.redkale.net.AsyncConnection;
import static org.redkale.source.DataSources.*;
import org.redkale.util.ObjectPool;
/**
*
* @author zhangjx
*/
public abstract class PoolTcpSource extends PoolSource<AsyncConnection> {
//ByteBuffer池
protected ObjectPool<ByteBuffer> bufferPool;
//线程池
protected ThreadPoolExecutor executor;
//TCP Channel组
protected AsynchronousChannelGroup group;
protected final ArrayBlockingQueue<AsyncConnection> connQueue;
public PoolTcpSource(String rwtype, ArrayBlockingQueue queue, Properties prop, Logger logger, ObjectPool<ByteBuffer> bufferPool, ThreadPoolExecutor executor) {
super(rwtype, prop, logger);
this.bufferPool = bufferPool;
this.executor = executor;
try {
this.group = AsynchronousChannelGroup.withThreadPool(executor);
} catch (IOException e) {
throw new RuntimeException(e);
}
this.connQueue = queue == null ? new ArrayBlockingQueue<>(this.maxconns) : queue;
}
@Override
public void offerConnection(final AsyncConnection conn) {
if (conn == null) return;
if (connQueue.offer(conn)) {
saveCounter.incrementAndGet();
usingCounter.decrementAndGet();
} else {
//usingCounter 会在close方法中执行
CompletableFuture<AsyncConnection> future = null;
try {
future = sendCloseCommand(conn);
} catch (Exception e) {
}
if (future == null) {
conn.dispose();
} else {
future.whenComplete((c, t) -> {
if (c != null) c.dispose();
});
}
}
}
@Override
public void change(Properties prop) {
String newurl = prop.getProperty(JDBC_URL);
String newuser = prop.getProperty(JDBC_USER, "");
String newpassword = prop.getProperty(JDBC_PWD, "");
if (this.url.equals(newurl) && this.username.equals(newuser) && this.password.equals(newpassword)) return;
this.url = newurl;
this.username = newuser;
this.password = newpassword;
parseAddressAndDbnameAndAttrs();
}
@Override
public final AsyncConnection poll() {
return pollAsync().join();
}
protected abstract ByteBuffer reqConnectBuffer(AsyncConnection conn);
protected abstract void respConnectBuffer(final ByteBuffer buffer, CompletableFuture<AsyncConnection> future, AsyncConnection conn);
@Override
public CompletableFuture<AsyncConnection> pollAsync() {
return pollAsync(0);
}
protected CompletableFuture<AsyncConnection> pollAsync(final int count) {
if (count >= 3) {
logger.log(Level.WARNING, "create datasource connection error");
CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
future.completeExceptionally(new SQLException("create datasource connection error"));
return future;
}
AsyncConnection conn0 = connQueue.poll();
if (conn0 != null && conn0.isOpen()) {
cycleCounter.incrementAndGet();
usingCounter.incrementAndGet();
return CompletableFuture.completedFuture(conn0);
}
if (usingCounter.get() >= maxconns && count < 2) {
return CompletableFuture.supplyAsync(() -> {
try {
return connQueue.poll(3, TimeUnit.SECONDS);
} catch (Exception t) {
return null;
}
}, executor).thenCompose((conn2) -> {
if (conn2 != null && conn2.isOpen()) {
cycleCounter.incrementAndGet();
return CompletableFuture.completedFuture(conn2);
}
return pollAsync(count + 1);
});
}
return AsyncConnection.createTCP(group, this.servaddr, this.readTimeoutSeconds, this.writeTimeoutSeconds).thenCompose(conn -> {
conn.beforeCloseListener((c) -> usingCounter.decrementAndGet());
CompletableFuture<AsyncConnection> future = new CompletableFuture();
final ByteBuffer buffer = reqConnectBuffer(conn);
if (buffer == null) {
final ByteBuffer rbuffer = bufferPool.get();
conn.read(rbuffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment2) {
if (result < 0) {
failed(new SQLException("Read Buffer Error"), attachment2);
return;
}
rbuffer.flip();
respConnectBuffer(rbuffer, future, conn);
}
@Override
public void failed(Throwable exc, Void attachment2) {
bufferPool.accept(rbuffer);
future.completeExceptionally(exc);
conn.dispose();
}
});
} else {
conn.write(buffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment1) {
if (result < 0) {
failed(new SQLException("Write Buffer Error"), attachment1);
return;
}
if (buffer.hasRemaining()) {
conn.write(buffer, attachment1, this);
return;
}
buffer.clear();
conn.read(buffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment2) {
if (result < 0) {
failed(new SQLException("Read Buffer Error"), attachment2);
return;
}
buffer.flip();
respConnectBuffer(buffer, future, conn);
}
@Override
public void failed(Throwable exc, Void attachment2) {
bufferPool.accept(buffer);
future.completeExceptionally(exc);
conn.dispose();
}
});
}
@Override
public void failed(Throwable exc, Void attachment1) {
bufferPool.accept(buffer);
future.completeExceptionally(exc);
conn.dispose();
}
});
}
return future;
}).whenComplete((c, t) -> {
if (t == null) {
creatCounter.incrementAndGet();
usingCounter.incrementAndGet();
}
});
}
@Override
public void close() {
connQueue.stream().forEach(x -> {
CompletableFuture<AsyncConnection> future = null;
try {
future = sendCloseCommand(x);
} catch (Exception e) {
}
if (future == null) {
x.dispose();
} else {
future.whenComplete((c, t) -> {
if (c != null) c.dispose();
});
}
});
}
protected abstract CompletableFuture<AsyncConnection> sendCloseCommand(final AsyncConnection conn);
}

View File

@@ -6,6 +6,7 @@
package org.redkale.source; package org.redkale.source;
import java.util.function.*; import java.util.function.*;
import org.redkale.util.Utility;
/** /**
* *
@@ -85,6 +86,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override @Override
public boolean test(Byte t) { public boolean test(Byte t) {
if (max < min) return t >= min;
return t >= min && t <= max; return t >= min && t <= max;
} }
@@ -129,6 +131,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override @Override
public boolean test(Short t) { public boolean test(Short t) {
if (max < min) return t >= min;
return t >= min && t <= max; return t >= min && t <= max;
} }
@@ -172,6 +175,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override @Override
public boolean test(Integer t) { public boolean test(Integer t) {
if (max < min) return t >= min;
return t >= min && t <= max; return t >= min && t <= max;
} }
@@ -195,6 +199,16 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
if (max != null) this.max = max; if (max != null) this.max = max;
} }
public static LongRange todayRange() {
long min = Utility.midnight();
return new LongRange(min, min + 24 * 60 * 60 * 1000 - 1);
}
public static LongRange yesterdayRange() {
long min = Utility.midnight(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
return new LongRange(min, min + 24 * 60 * 60 * 1000 - 1);
}
@Override @Override
public Long getMin() { public Long getMin() {
return min; return min;
@@ -215,6 +229,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override @Override
public boolean test(Long t) { public boolean test(Long t) {
if (max < min) return t >= min;
return t >= min && t <= max; return t >= min && t <= max;
} }
@@ -258,6 +273,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override @Override
public boolean test(Float t) { public boolean test(Float t) {
if (max < min) return t >= min;
return t >= min && t <= max; return t >= min && t <= max;
} }
@@ -301,6 +317,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override @Override
public boolean test(Double t) { public boolean test(Double t) {
if (max < min) return t >= min;
return t >= min && t <= max; return t >= min && t <= max;
} }

View File

@@ -0,0 +1,22 @@
/*
* 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.source;
/**
* 自定义的DataSource加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface SourceLoader {
public String dbtype();
public Class<? extends DataSource> dataSourceClass();
}

View File

@@ -487,12 +487,22 @@ public abstract class AnyValue {
public byte getByteValue(String name, byte defaultValue) { public byte getByteValue(String name, byte defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : Byte.decode(value); if (value == null || value.length() == 0) return defaultValue;
try {
return Byte.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public byte getByteValue(int radix, String name, byte defaultValue) { public byte getByteValue(int radix, String name, byte defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : (radix == 10 ? Byte.decode(value) : Byte.parseByte(value, radix)); if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Byte.decode(value) : Byte.parseByte(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public char getCharValue(String name) { public char getCharValue(String name) {
@@ -510,12 +520,22 @@ public abstract class AnyValue {
public short getShortValue(String name, short defaultValue) { public short getShortValue(String name, short defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : Short.decode(value); if (value == null || value.length() == 0) return defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public short getShortValue(int radix, String name, short defaultValue) { public short getShortValue(int radix, String name, short defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public int getIntValue(String name) { public int getIntValue(String name) {
@@ -524,12 +544,22 @@ public abstract class AnyValue {
public int getIntValue(String name, int defaultValue) { public int getIntValue(String name, int defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : Integer.decode(value); if (value == null || value.length() == 0) return defaultValue;
try {
return Integer.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public int getIntValue(int radix, String name, int defaultValue) { public int getIntValue(int radix, String name, int defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public long getLongValue(String name) { public long getLongValue(String name) {
@@ -538,12 +568,22 @@ public abstract class AnyValue {
public long getLongValue(String name, long defaultValue) { public long getLongValue(String name, long defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : Long.decode(value); if (value == null || value.length() == 0) return defaultValue;
try {
return Long.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public long getLongValue(int radix, String name, long defaultValue) { public long getLongValue(int radix, String name, long defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public float getFloatValue(String name) { public float getFloatValue(String name) {
@@ -552,7 +592,12 @@ public abstract class AnyValue {
public float getFloatValue(String name, float defaultValue) { public float getFloatValue(String name, float defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : Float.parseFloat(value); if (value == null || value.length() == 0) return defaultValue;
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public double getDoubleValue(String name) { public double getDoubleValue(String name) {
@@ -561,7 +606,12 @@ public abstract class AnyValue {
public double getDoubleValue(String name, double defaultValue) { public double getDoubleValue(String name, double defaultValue) {
String value = getValue(name); String value = getValue(name);
return value == null || value.length() == 0 ? defaultValue : Double.parseDouble(value); if (value == null || value.length() == 0) return defaultValue;
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
public String getValue(String name, String defaultValue) { public String getValue(String name, String defaultValue) {

View File

@@ -0,0 +1,325 @@
/*
* 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.util;
import java.nio.*;
import java.util.*;
/**
* 以ByteBuffer为数据载体的Reader <br>
* 注意最小可读空间至少是8
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class ByteBufferReader {
private ByteBuffer[] buffers;
private int currIndex;
private ByteBuffer currBuffer;
private final boolean bigEndian;
public ByteBufferReader(Collection<ByteBuffer> buffers) {
Objects.requireNonNull(buffers);
this.buffers = buffers.toArray(new ByteBuffer[buffers.size()]);
this.currBuffer = this.buffers[0];
this.currIndex = 0;
this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN;
}
public ByteBufferReader(ByteBuffer[] buffers) {
Objects.requireNonNull(buffers);
this.buffers = buffers;
this.currBuffer = this.buffers[0];
this.currIndex = 0;
this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN;
}
public ByteBufferReader(ByteBuffer buffer) {
Objects.requireNonNull(buffer);
this.buffers = new ByteBuffer[]{buffer};
this.currBuffer = this.buffers[0];
this.currIndex = 0;
this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN;
}
public static ByteBufferReader create(ByteBuffer buffer) {
return new ByteBufferReader(buffer);
}
public static ByteBufferReader create(Collection<ByteBuffer> buffers) {
return new ByteBufferReader(buffers);
}
public static ByteBufferReader create(ByteBuffer[] buffers) {
return new ByteBufferReader(buffers);
}
public boolean hasRemaining() {
return this.currBuffer.hasRemaining();
}
public byte get() {
ByteBuffer buf = this.currBuffer;
if (!buf.hasRemaining()) {
buf = this.buffers[++this.currIndex];
this.currBuffer = buf;
}
return this.currBuffer.get();
}
public short getShort() {
ByteBuffer buf = this.currBuffer;
int remain = buf.remaining();
if (remain >= 2) return buf.getShort();
if (remain == 0) {
buf = this.buffers[++this.currIndex];
this.currBuffer = buf;
return buf.getShort();
}
if (bigEndian) return (short) ((buf.get() << 8) | (get() & 0xff));
return (short) ((buf.get() & 0xff) | (get() << 8));
}
public int getInt() {
ByteBuffer buf = this.currBuffer;
int remain = buf.remaining();
if (remain >= 4) return buf.getInt();
if (remain == 0) {
buf = this.buffers[++this.currIndex];
this.currBuffer = buf;
return buf.getInt();
}
if (bigEndian) {
if (remain == 1) {
return ((buf.get() << 24)
| ((get() & 0xff) << 16)
| ((get() & 0xff) << 8)
| ((get() & 0xff)));
}
if (remain == 2) {
return ((buf.get() << 24)
| ((buf.get() & 0xff) << 16)
| ((get() & 0xff) << 8)
| ((get() & 0xff)));
}
//remain == 3
return ((buf.get() << 24)
| ((buf.get() & 0xff) << 16)
| ((buf.get() & 0xff) << 8)
| ((get() & 0xff)));
}
if (remain == 1) {
return ((buf.get() & 0xff)
| ((get() & 0xff) << 8)
| ((get() & 0xff) << 16)
| ((get() << 24)));
}
if (remain == 2) {
return ((buf.get() & 0xff)
| ((buf.get() & 0xff) << 8)
| ((get() & 0xff) << 16)
| ((get() << 24)));
}
//remain == 3
return ((buf.get()) & 0xff)
| ((buf.get() & 0xff) << 8)
| ((buf.get() & 0xff) << 16)
| ((get() << 24));
}
public float getFloat() {
return Float.intBitsToFloat(getInt());
}
public long getLong() {
ByteBuffer buf = this.currBuffer;
int remain = buf.remaining();
if (remain >= 8) return buf.getLong();
if (remain == 0) {
buf = this.buffers[++this.currIndex];
this.currBuffer = buf;
return buf.getLong();
}
if (bigEndian) {
if (remain == 1) {
return ((((long) buf.get()) << 56)
| (((long) get() & 0xff) << 48)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
if (remain == 2) {
return ((((long) buf.get()) << 56)
| (((long) buf.get() & 0xff) << 48)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
if (remain == 3) {
return ((((long) buf.get()) << 56)
| (((long) buf.get() & 0xff) << 48)
| (((long) buf.get() & 0xff) << 40)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
if (remain == 4) {
return ((((long) buf.get()) << 56)
| (((long) buf.get() & 0xff) << 48)
| (((long) buf.get() & 0xff) << 40)
| (((long) buf.get() & 0xff) << 32)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
if (remain == 5) {
return ((((long) buf.get()) << 56)
| (((long) buf.get() & 0xff) << 48)
| (((long) buf.get() & 0xff) << 40)
| (((long) buf.get() & 0xff) << 32)
| (((long) buf.get() & 0xff) << 24)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
if (remain == 6) {
return ((((long) buf.get()) << 56)
| (((long) buf.get() & 0xff) << 48)
| (((long) buf.get() & 0xff) << 40)
| (((long) buf.get() & 0xff) << 32)
| (((long) buf.get() & 0xff) << 24)
| (((long) buf.get() & 0xff) << 16)
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
//remain == 7
return ((((long) buf.get()) << 56)
| (((long) buf.get() & 0xff) << 48)
| (((long) buf.get() & 0xff) << 40)
| (((long) buf.get() & 0xff) << 32)
| (((long) buf.get() & 0xff) << 24)
| (((long) buf.get() & 0xff) << 16)
| (((long) buf.get() & 0xff) << 8)
| (((long) get() & 0xff)));
}
if (remain == 1) {
return ((((long) buf.get() & 0xff))
| (((long) get() & 0xff) << 8)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 48)
| (((long) get()) << 56));
}
if (remain == 2) {
return ((((long) buf.get() & 0xff))
| (((long) buf.get() & 0xff) << 8)
| (((long) get() & 0xff) << 16)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 48)
| (((long) get()) << 56));
}
if (remain == 3) {
return ((((long) buf.get() & 0xff))
| (((long) buf.get() & 0xff) << 8)
| (((long) buf.get() & 0xff) << 16)
| (((long) get() & 0xff) << 24)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 48)
| (((long) get()) << 56));
}
if (remain == 4) {
return ((((long) buf.get() & 0xff))
| (((long) buf.get() & 0xff) << 8)
| (((long) buf.get() & 0xff) << 16)
| (((long) buf.get() & 0xff) << 24)
| (((long) get() & 0xff) << 32)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 48)
| (((long) get()) << 56));
}
if (remain == 5) {
return ((((long) buf.get() & 0xff))
| (((long) buf.get() & 0xff) << 8)
| (((long) buf.get() & 0xff) << 16)
| (((long) buf.get() & 0xff) << 24)
| (((long) buf.get() & 0xff) << 32)
| (((long) get() & 0xff) << 40)
| (((long) get() & 0xff) << 48)
| (((long) get()) << 56));
}
if (remain == 6) {
return ((((long) buf.get() & 0xff))
| (((long) buf.get() & 0xff) << 8)
| (((long) buf.get() & 0xff) << 16)
| (((long) buf.get() & 0xff) << 24)
| (((long) buf.get() & 0xff) << 32)
| (((long) buf.get() & 0xff) << 40)
| (((long) get() & 0xff) << 48)
| (((long) get()) << 56));
}
//remain == 7
return ((((long) buf.get() & 0xff))
| (((long) buf.get() & 0xff) << 8)
| (((long) buf.get() & 0xff) << 16)
| (((long) buf.get() & 0xff) << 24)
| (((long) buf.get() & 0xff) << 32)
| (((long) buf.get() & 0xff) << 40)
| (((long) buf.get() & 0xff) << 48)
| (((long) get()) << 56));
}
public double getDouble() {
return Double.longBitsToDouble(getLong());
}
public ByteBufferReader get(byte[] dst) {
return get(dst, 0, dst.length);
}
public ByteBufferReader get(byte[] dst, int offset, int length) {
ByteBuffer buf = this.currBuffer;
int remain = buf.remaining();
if (remain >= length) {
buf.get(dst, offset, length);
return this;
}
buf.get(dst, offset, remain);
this.currBuffer = this.buffers[++this.currIndex];
return get(dst, offset + remain, length - remain);
}
public ByteBufferReader skip(int size) {
ByteBuffer buf = this.currBuffer;
int remain = buf.remaining();
if (remain >= size) {
buf.position(buf.position() + size);
return this;
}
buf.position(buf.position() + remain);
this.currBuffer = this.buffers[++this.currIndex];
return skip(size - remain);
}
}

View File

@@ -0,0 +1,191 @@
/*
* 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.util;
import java.nio.*;
import java.util.function.Supplier;
/**
* 以ByteBuffer为数据载体的Writer
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class ByteBufferWriter {
private final Supplier<ByteBuffer> supplier;
private ByteBuffer[] buffers;
private int position;
private boolean bigEndian = true;
protected ByteBufferWriter(Supplier<ByteBuffer> supplier) {
this.supplier = supplier;
}
public static ByteBufferWriter create(Supplier<ByteBuffer> supplier) {
return new ByteBufferWriter(supplier);
}
private ByteBuffer getLastBuffer(int size) {
if (this.buffers == null) {
ByteBuffer buf = supplier.get();
this.bigEndian = buf.order() == ByteOrder.BIG_ENDIAN;
this.buffers = Utility.append(this.buffers, buf);
return buf;
} else if (this.buffers[this.buffers.length - 1].remaining() < size) {
ByteBuffer buf = supplier.get();
this.buffers = Utility.append(this.buffers, buf);
return buf;
}
return this.buffers[this.buffers.length - 1];
}
public ByteBuffer[] toBuffers() {
if (buffers == null) return new ByteBuffer[0];
for (ByteBuffer buf : this.buffers) {
buf.flip();
}
return this.buffers;
}
public int position() {
return position;
}
public ByteBufferWriter put(byte b) {
getLastBuffer(1).put(b);
position++;
return this;
}
public ByteBufferWriter putShort(short value) {
getLastBuffer(2).putShort(value);
position += 2;
return this;
}
public ByteBufferWriter putInt(int value) {
getLastBuffer(4).putInt(value);
position += 4;
return this;
}
//重新设置指定位置的值
public ByteBufferWriter putInt(final int index, int value) {
int start = 0;
ByteBuffer[] buffs = this.buffers;
for (int i = 0; i < buffs.length; i++) {
int pos = buffs[i].position();
if (pos + start > index) {
int r = pos + start - index;
if (r >= 4) {
buffs[i].putInt(index - start, value);
return this;
} else {
byte b1 = bigEndian ? (byte) ((value >> 24) & 0xFF) : (byte) (value & 0xFF);
byte b2 = bigEndian ? (byte) ((value >> 16) & 0xFF) : (byte) ((value >> 8) & 0xFF);
byte b3 = bigEndian ? (byte) ((value >> 8) & 0xFF) : (byte) ((value >> 16) & 0xFF);
byte b4 = bigEndian ? (byte) (value & 0xFF) : (byte) ((value >> 24) & 0xFF);
if (r == 3) {
buffs[i].put(index - start, b1);
buffs[i].put(index - start + 1, b2);
buffs[i].put(index - start + 2, b3);
buffs[i + 1].put(0, b4);
} else if (r == 2) {
buffs[i].put(index - start, b1);
buffs[i].put(index - start + 1, b2);
buffs[i + 1].put(0, b3);
buffs[i + 1].put(1, b4);
} else if (r == 1) {
buffs[i].put(index - start, b1);
buffs[i + 1].put(0, b2);
buffs[i + 1].put(1, b3);
buffs[i + 1].put(2, b4);
}
return this;
}
} else {
start += pos;
}
}
throw new ArrayIndexOutOfBoundsException(index);
}
// public static void main(String[] args) throws Throwable {
// ObjectPool<ByteBuffer> pool = new ObjectPool<>(20, (p) -> ByteBuffer.allocate(10), (ByteBuffer t) -> t.clear(), (ByteBuffer t) -> false);
// ByteBufferWriter writer = ByteBufferWriter.create(pool);
// for (int i = 1; i <= 18; i++) {
// writer.put((byte) i);
// }
// System.out.println(Arrays.toString(toBytes(writer.toBuffers())));
//
// writer = ByteBufferWriter.create(pool);
// for (int i = 1; i <= 18; i++) {
// writer.put((byte) i);
// }
// int value = 0x223344;
// byte[] b4 = new byte[]{(byte) ((value >> 24) & 0xFF), (byte) ((value >> 16) & 0xFF), (byte) ((value >> 8) & 0xFF), (byte) (value & 0xFF)};
// writer.putInt(9, value);
// System.out.println(Arrays.toString(b4));
// System.out.println(Arrays.toString(toBytes(writer.toBuffers())));
// }
public ByteBufferWriter putFloat(float value) {
getLastBuffer(4).putFloat(value);
position += 4;
return this;
}
public ByteBufferWriter putLong(long value) {
getLastBuffer(8).putLong(value);
position += 8;
return this;
}
public ByteBufferWriter putDouble(double value) {
getLastBuffer(8).putDouble(value);
position += 8;
return this;
}
public ByteBufferWriter put(byte[] src) {
return put(src, 0, src.length);
}
public ByteBufferWriter put(byte[] src, int offset, int length) {
ByteBuffer buf = getLastBuffer(1);
int remain = buf.remaining();
if (remain >= length) {
buf.put(src, offset, length);
position += length;
} else {
buf.put(src, offset, remain);
position += remain;
put(src, offset + remain, length - remain);
}
return this;
}
public static byte[] toBytes(ByteBuffer[] buffers) {
if (buffers == null) return null;
int size = 0;
for (ByteBuffer buffer : buffers) {
size += buffer.remaining();
}
byte[] bs = new byte[size];
int index = 0;
for (ByteBuffer buffer : buffers) {
int remain = buffer.remaining();
buffer.get(bs, index, remain);
index += remain;
}
return bs;
}
}

View File

@@ -105,7 +105,7 @@ public interface Creator<T> {
if (this.started) return null; if (this.started) return null;
this.started = true; this.started = true;
//返回的List中参数列表可能会比方法参数量多因为方法内的临时变量也会存入list中 所以需要list的元素集合比方法的参数多 //返回的List中参数列表可能会比方法参数量多因为方法内的临时变量也会存入list中 所以需要list的元素集合比方法的参数多
return new MethodVisitor(Opcodes.ASM5) { return new MethodVisitor(Opcodes.ASM6) {
@Override @Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
if (index < 1) return; if (index < 1) return;
@@ -139,7 +139,7 @@ public interface Creator<T> {
return null; return null;
} }
final List<String> fieldnames = new ArrayList<>(); final List<String> fieldnames = new ArrayList<>();
new ClassReader(out.toByteArray()).accept(new SimpleClassVisitor(Opcodes.ASM5, fieldnames, constructorDesc), 0); new ClassReader(out.toByteArray()).accept(new SimpleClassVisitor(Opcodes.ASM6, fieldnames, constructorDesc), 0);
if (fieldnames.isEmpty()) return null; if (fieldnames.isEmpty()) return null;
if (paramcount == fieldnames.size()) { if (paramcount == fieldnames.size()) {
return getConstructorField(clazz, paramcount, fieldnames.toArray(new String[fieldnames.size()])); return getConstructorField(clazz, paramcount, fieldnames.toArray(new String[fieldnames.size()]));

View File

@@ -17,7 +17,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* *
* @author zhangjx * @author zhangjx
*/ */
@Inherited
@Documented @Documented
@Target({TYPE}) @Target({TYPE})
@Retention(RUNTIME) @Retention(RUNTIME)

View File

@@ -17,7 +17,7 @@ public final class Redkale {
} }
public static String getDotedVersion() { public static String getDotedVersion() {
return "1.9.2"; return "1.9.3";
} }
public static int getMajorVersion() { public static int getMajorVersion() {

View File

@@ -485,7 +485,7 @@ public final class ResourceFactory {
if (re.value != null) list.add((A) re.value); if (re.value != null) list.add((A) re.value);
} }
} }
if (parent != null) query(list, clazz); if (parent != null) parent.query(list, clazz);
return list; return list;
} }
@@ -502,7 +502,7 @@ public final class ResourceFactory {
} }
} }
} }
if (parent != null) query(list, predicate); if (parent != null) parent.query(list, predicate);
return list; return list;
} }

View File

@@ -10,6 +10,8 @@ import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler; import java.nio.channels.CompletionHandler;
import java.nio.charset.*; import java.nio.charset.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.*;
import java.time.*; import java.time.*;
import java.util.*; import java.util.*;
import java.util.function.*; import java.util.function.*;
@@ -31,8 +33,6 @@ public final class Utility {
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"; private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS";
private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/** /**
@@ -566,6 +566,22 @@ public final class Utility {
return false; return false;
} }
/**
* 判断指定值是否包含指定的数组中包含返回true
*
* @param values 集合
* @param value 单值
*
* @return boolean
*/
public static boolean contains(short[] values, short value) {
if (values == null) return false;
for (short v : values) {
if (v == value) return true;
}
return false;
}
/** /**
* 判断指定值是否包含指定的数组中包含返回true * 判断指定值是否包含指定的数组中包含返回true
* *
@@ -957,6 +973,64 @@ public final class Utility {
return ld.atStartOfDay(zid).toInstant().toEpochMilli(); return ld.atStartOfDay(zid).toInstant().toEpochMilli();
} }
/**
* MD5加密
*
* @param bs 待加密数据
*
* @return md5值
*/
public static String md5Hex(byte[] bs) {
return binToHexString(md5Bytes(bs));
}
/**
* MD5加密
*
* @param bs 待加密数据
*
* @return md5值
*/
public static byte[] md5Bytes(byte[] bs) {
if (bs == null) return null;
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
return md5.digest(bs);
}
/**
* MD5加密
*
* @param str 待加密数据
*
* @return md5值
*/
public static String md5Hex(String str) {
return binToHexString(md5Bytes(str));
}
/**
* MD5加密
*
* @param str 待加密数据
*
* @return md5值
*/
public static byte[] md5Bytes(String str) {
if (str == null) return null;
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
return md5.digest(str.getBytes());
}
/** /**
* 将字节数组转换为16进制字符串 * 将字节数组转换为16进制字符串
* *
@@ -1602,41 +1676,48 @@ public final class Utility {
public static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, int timeout, Map<String, String> headers, String body) throws IOException { public static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, int timeout, Map<String, String> headers, String body) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(timeout > 0 ? timeout : 3000); boolean opening = true;
conn.setReadTimeout(timeout > 0 ? timeout : 3000); try {
if (conn instanceof HttpsURLConnection) { conn.setConnectTimeout(timeout > 0 ? timeout : 3000);
HttpsURLConnection httpsconn = ((HttpsURLConnection) conn); conn.setReadTimeout(timeout > 0 ? timeout : 3000);
httpsconn.setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory()); if (conn instanceof HttpsURLConnection) {
httpsconn.setHostnameVerifier(defaultVerifier); HttpsURLConnection httpsconn = ((HttpsURLConnection) conn);
} httpsconn.setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory());
conn.setRequestMethod(method); httpsconn.setHostnameVerifier(defaultVerifier);
if (headers != null) {
for (Map.Entry<String, String> en : headers.entrySet()) { //不用forEach是为了兼容JDK 6
conn.setRequestProperty(en.getKey(), en.getValue());
} }
conn.setRequestMethod(method);
if (headers != null) {
for (Map.Entry<String, String> en : headers.entrySet()) { //不用forEach是为了兼容JDK 6
conn.setRequestProperty(en.getKey(), en.getValue());
}
}
if (body != null) {
conn.setDoInput(true);
conn.setDoOutput(true);
conn.getOutputStream().write(body.getBytes(UTF_8));
}
conn.setUseCaches(false);
conn.connect();
int rs = conn.getResponseCode();
if (rs == 301 || rs == 302) {
String newurl = conn.getHeaderField("Location");
conn.disconnect();
opening = false;
return remoteHttpContent(ctx, method, newurl, timeout, headers, body);
}
InputStream in = (rs < 400 || rs == 404) && rs != 405 ? conn.getInputStream() : conn.getErrorStream();
if ("gzip".equalsIgnoreCase(conn.getContentEncoding())) in = new GZIPInputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
byte[] bytes = new byte[1024];
int pos;
while ((pos = in.read(bytes)) != -1) {
out.write(bytes, 0, pos);
}
in.close();
return out;
} finally {
if (opening) conn.disconnect();
} }
if (body != null) {
conn.setDoInput(true);
conn.setDoOutput(true);
conn.getOutputStream().write(body.getBytes(UTF_8));
}
conn.connect();
int rs = conn.getResponseCode();
if (rs == 301 || rs == 302) {
String newurl = conn.getHeaderField("Location");
conn.disconnect();
return remoteHttpContent(ctx, method, newurl, timeout, headers, body);
}
InputStream in = (rs < 400 || rs == 404) && rs != 405 ? conn.getInputStream() : conn.getErrorStream();
if ("gzip".equalsIgnoreCase(conn.getContentEncoding())) in = new GZIPInputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
byte[] bytes = new byte[1024];
int pos;
while ((pos = in.read(bytes)) != -1) {
out.write(bytes, 0, pos);
}
conn.disconnect();
return out;
} }
public static String read(InputStream in) throws IOException { public static String read(InputStream in) throws IOException {

View File

@@ -16,8 +16,8 @@ public class AsmCreator {
public static void main(String[] args) throws Throwable { public static void main(String[] args) throws Throwable {
boolean realasm = false; //从http://forge.ow2.org/projects/asm/ 下载最新asm的src放在 srcasmroot 目录下 boolean realasm = false; //从http://forge.ow2.org/projects/asm/ 下载最新asm的src放在 srcasmroot 目录下
File srcasmroot = new File("D:/JAVA/JDK源码/JDK9源码/java.base/jdk/internal/org/objectweb/asm"); File srcasmroot = new File("D:/JAVA/JDK源码/jdk/internal/org/objectweb/asm");
if(realasm) srcasmroot = new File("D:/JAVA/JDK源码/org/objectweb/asm"); if (realasm) srcasmroot = new File("D:/JAVA/JDK源码/org/objectweb/asm");
File destasmroot = new File("D:/Java-Projects/RedkaleProject/src/org/redkale/asm"); File destasmroot = new File("D:/Java-Projects/RedkaleProject/src/org/redkale/asm");
String line = null; String line = null;
LineNumberReader txtin = new LineNumberReader(new FileReader(new File(destasmroot, "asm.txt"))); LineNumberReader txtin = new LineNumberReader(new FileReader(new File(destasmroot, "asm.txt")));
@@ -32,5 +32,6 @@ public class AsmCreator {
out.write(content.replace("jdk.internal.org.objectweb", "org.redkale").replace("org.objectweb", "org.redkale").getBytes()); out.write(content.replace("jdk.internal.org.objectweb", "org.redkale").replace("org.objectweb", "org.redkale").getBytes());
out.close(); out.close();
} }
//需要屏蔽ClassReader中判断checks the class version的部分
} }
} }