338 Commits
1.8.8 ... 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
Redkale
bfbcab4009 2018-04-03 08:29:42 +08:00
Redkale
742c352080 2018-04-02 20:53:23 +08:00
Redkale
643827354c 2018-04-02 19:29:55 +08:00
Redkale
2f7bebfc17 2018-04-02 18:48:56 +08:00
Redkale
7bf8d60ddf 2018-04-02 16:28:21 +08:00
Redkale
09c51b6b4e 2018-04-02 16:26:35 +08:00
Redkale
84f6ce58a0 2018-04-02 14:48:23 +08:00
Redkale
9e90ae5285 2018-04-02 14:08:20 +08:00
Redkale
93b1c9f0d9 2018-04-02 13:22:45 +08:00
Redkale
dab8ed8ceb 2018-03-31 11:27:11 +08:00
Redkale
f016e49111 2018-03-31 11:15:58 +08:00
Redkale
b403f22284 2018-03-31 10:46:21 +08:00
Redkale
ccb9ac93f7 ConvertFactory增加registerIgnoreAll配置 2018-03-31 10:33:57 +08:00
Redkale
72935f1ebe 2018-03-31 10:28:33 +08:00
Redkale
1f7d46219a net和WebSokcet模块增加Cryptor功能 2018-03-30 20:38:29 +08:00
Redkale
9094f76de2 Utility增加contains系列方法 2018-03-30 19:53:20 +08:00
Redkale
d67cbeb3f2 2018-03-30 17:42:18 +08:00
Redkale
90562ebd04 2018-03-30 17:25:57 +08:00
Redkale
b14e14659c 2018-03-30 08:56:00 +08:00
Redkale
84a15afc9a 2018-03-30 08:39:15 +08:00
Redkale
8c1aba5608 Transport.pollConnection的负载均衡策略改成轮询 2018-03-29 20:21:31 +08:00
Redkale
a72c689f07 2018-03-29 19:14:20 +08:00
Redkale
1d74f34575 2018-03-29 19:10:50 +08:00
Redkale
a24092d391 TransportStrategy增加offerConnection方法 2018-03-29 19:02:16 +08:00
Redkale
097ae701c1 2018-03-29 11:18:22 +08:00
Redkale
e250a593a7 完善Transport.pollConnection中连接池功能 2018-03-29 11:14:13 +08:00
Redkale
1547e6b714 Reproduce支持public field和setter、getter混合 2018-03-29 08:33:55 +08:00
Redkale
9f14224269 Reproduce增加不同字段名可以赋值的功能 2018-03-28 14:55:40 +08:00
Redkale
ecc54f9bd5 2018-03-28 09:32:21 +08:00
Redkale
521ee56d31 2018-03-28 08:59:47 +08:00
Redkale
f9da063532 2018-03-24 11:37:28 +08:00
Redkale
5070c27f6a 2018-03-24 11:34:24 +08:00
Redkale
c0664bb0a9 2018-03-24 11:28:25 +08:00
Redkale
f8c92f1ec4 2018-03-24 11:18:48 +08:00
Redkale
01930cfdc8 屏蔽sun.misc.Unsafe 2018-03-24 11:15:38 +08:00
Redkale
e76e321765 删掉过期的AsyncHandler 2018-03-21 14:14:30 +08:00
Redkale
99d2db31f7 2018-03-20 19:58:24 +08:00
Redkale
fa9015447b Redkale 1.9.2 开始 2018-03-20 19:57:19 +08:00
Redkale
735ad0908b 2018-03-17 10:44:03 +08:00
Redkale
a080a6a8cc 2018-03-16 08:45:42 +08:00
Redkale
2cbc51cfdf 2018-03-14 21:21:23 +08:00
Redkale
8631b4bdf5 Entity数据库实体类支持AtomicInteger、AtomicLong字段类型 2018-03-14 17:36:29 +08:00
Redkale
285cb86891 RetResult增加successFuture方法 2018-03-14 14:56:42 +08:00
Redkale
bf535a7161 2018-03-13 09:28:13 +08:00
Redkale
34e37471b8 2018-03-12 21:50:17 +08:00
Redkale
e223548b23 2018-03-12 21:41:36 +08:00
Redkale
3a13d242f6 2018-03-12 20:54:24 +08:00
Redkale
204e6ec99f 2018-03-12 10:04:53 +08:00
Redkale
7e348782e4 2018-03-09 19:05:37 +08:00
Redkale
a4cbe7db17 2018-03-09 09:33:42 +08:00
Redkale
f8101acb4b 2018-03-07 19:55:02 +08:00
Redkale
9ed2d59317 2018-03-07 12:50:26 +08:00
Redkale
0329ad7832 2018-03-07 12:13:06 +08:00
Redkale
53df45456f 删掉对sun.misc.Unsafe的依赖和替换过期方法Class.newInstance() 2018-03-07 11:31:11 +08:00
Redkale
93698bacff Redkale 1.9.1 开始 2018-03-07 08:30:15 +08:00
Redkale
9fbf63f720 2018-02-28 08:58:34 +08:00
Redkale
b49c334f9f AsyncConnection增加SSLContext属性,便于以后增加SSL功能 2018-02-28 08:56:47 +08:00
Redkale
a374e1278b 2018-02-11 15:57:13 +08:00
Redkale
0a16fb85bd 2018-02-11 15:07:38 +08:00
Redkale
419d5bae09 2018-02-11 14:14:52 +08:00
Redkale
d78f3565de 2018-02-11 13:36:59 +08:00
Redkale
4814e7db74 2018-02-11 12:58:03 +08:00
Redkale
3470fd35d5 2018-02-11 12:53:47 +08:00
Redkale
2915bae474 2018-02-11 12:47:10 +08:00
Redkale
ae9be7ac25 2018-02-11 11:23:36 +08:00
Redkale
4b9cb3716b 更改临时文件生成的目录结构 2018-02-09 09:20:01 +08:00
Redkale
38bd9fb204 2018-02-08 16:37:18 +08:00
Redkale
433c0db4fb 2018-02-08 16:34:10 +08:00
Redkale
ccebc18b85 2018-02-08 16:12:14 +08:00
Redkale
6fb85eb9ca 2018-02-08 15:56:08 +08:00
Redkale
c2dad0807f 2018-02-08 15:30:35 +08:00
Redkale
d6ba4a1ab7 2018-02-08 14:41:02 +08:00
Redkale
f9fae91c71 2018-02-08 14:13:38 +08:00
Redkale
fdb7ca29bc SNCP、REST支持部分含泛型继承的类 2018-02-08 10:44:38 +08:00
Redkale
722ab55591 2018-02-07 19:32:45 +08:00
Redkale
450b390ac4 Convert支持部分TypeVariable 2018-02-07 14:50:43 +08:00
Redkale
39eca5d39e 2018-02-06 16:14:39 +08:00
Redkale
4a4b9ad1d2 2018-02-02 10:46:17 +08:00
Redkale
ea01647200 2018-02-02 10:42:31 +08:00
Redkale
1f8faa8afd AsmMethodVisitor类迁移 2018-02-02 09:19:42 +08:00
Redkale
2b78d145ea 2018-02-01 18:39:14 +08:00
Redkale
e69b8bcd64 2018-02-01 09:42:27 +08:00
Redkale
3dcd85e902 Context增加SSLContext属性 2018-01-31 10:45:45 +08:00
Redkale
deab165c7f 增加java.util.logging.FileHandler.denyreg属性配置 2018-01-31 10:43:22 +08:00
Redkale
e405c4dc15 2018-01-30 13:39:27 +08:00
Redkale
c5260584fa 2018-01-30 13:28:57 +08:00
Redkale
15cd16e771 2018-01-30 11:27:14 +08:00
Redkale
11d8d36c12 2018-01-30 09:58:05 +08:00
Redkale
d542ac9110 2018-01-30 09:12:08 +08:00
Redkale
3d0e4ee7d7 2018-01-30 09:01:19 +08:00
Redkale
379a399d29 2018-01-29 21:39:53 +08:00
Redkale
26f0324573 2018-01-29 21:31:59 +08:00
Redkale
a71f1a17dd 2018-01-29 21:26:35 +08:00
Redkale
9f4f459ea8 2018-01-29 16:16:50 +08:00
Redkale
adafb9645c 2018-01-29 14:27:14 +08:00
Redkale
09c825bbcd 2018-01-29 14:11:25 +08:00
Redkale
255c1d945c 增加模板引擎规范API 2018-01-29 13:56:06 +08:00
Redkale
c5eb49d12c 2018-01-27 09:27:07 +08:00
Redkale
ad690724a6 2018-01-27 09:17:16 +08:00
Redkale
1573e79041 WebSocketParam增加getNames方法 2018-01-26 20:29:22 +08:00
Redkale
4d8026ec99 2018-01-26 11:33:53 +08:00
Redkale
5c1c2b18e4 WebSocket实现preOnMessage功能 2018-01-26 11:10:45 +08:00
Redkale
66baca51d7 CacheSource增加getIfAbsent系列方法 2018-01-25 14:24:28 +08:00
Redkale
355441e795 Creator的getConstructorField方法增加对子类字段的支持 2018-01-24 10:50:08 +08:00
Redkale
902fd70eb4 ConvertDisabled增加ConvertType属性 2018-01-16 15:58:24 +08:00
Redkale
4ce7eee4aa 2018-01-16 15:47:57 +08:00
Redkale
6a73c2a697 2018-01-16 15:44:40 +08:00
Redkale
f713669d1f 2018-01-16 14:24:00 +08:00
Redkale
d5365af373 2018-01-16 11:49:58 +08:00
Redkale
ccffa0d5c2 修复单个WebSocket并发问题 2018-01-15 15:11:25 +08:00
Redkale
0ee5b911d2 修复单个WebSocket并发问题 2018-01-15 14:50:03 +08:00
Redkale
c92d17abbc 2018-01-14 16:39:21 +08:00
Redkale
ef5ef4ed72 2018-01-13 10:07:23 +08:00
Redkale
ba5f4a24c7 bufferCapacity的默认值统一调整成32K 2018-01-13 10:05:13 +08:00
Redkale
1cd5fe0b02 增加SERVER_EXECUTOR线程池的资源注入 2018-01-12 09:14:10 +08:00
Redkale
21af487aab 限制标记@RestOnMessage方法的条件 2018-01-11 19:13:14 +08:00
Redkale
f254519a1c 2018-01-11 14:28:40 +08:00
Redkale
26fc039aa5 去掉java.beans.ConstructorProperties,使用org.redkale.util.ConstructorParameters 代替 2018-01-11 11:05:39 +08:00
Redkale
7d3e4529af 2018-01-10 10:22:46 +08:00
Redkale
bcefaee3e9 2018-01-09 16:48:39 +08:00
Redkale
3933eaa1cf 2018-01-08 19:36:26 +08:00
Redkale
28251435e4 增加ConvertDisabled功能 2018-01-08 11:23:05 +08:00
Redkale
d29dc320c3 RetResult增加clearAttach方法 2018-01-06 14:04:33 +08:00
Redkale
d14a939acb 2018-01-05 18:41:03 +08:00
Redkale
e20619e0c9 自动删除0字节的日志文件 2018-01-05 08:42:15 +08:00
Redkale
8ff829f1b1 2018-01-05 08:24:11 +08:00
Redkale
402255ba0d Flipper的limit可以设置为0 2018-01-04 20:02:51 +08:00
Redkale
7250bf8047 Update README.md 2018-01-04 08:27:11 +08:00
Redkale
c76436cd3c Update README.md 2018-01-03 19:35:49 +08:00
Redkale
9b85562595 Update README.md 2018-01-03 19:35:13 +08:00
Redkale
4798ddfe19 2018-01-03 19:17:27 +08:00
Redkale
dd197cfa24 将JDK内置的jdk.internal.org.objectweb.asm包拷贝一份进redkale 2018-01-03 19:04:58 +08:00
Redkale
844b9c19e5 2018-01-03 19:01:34 +08:00
Redkale
5f6f1ebc44 2018-01-03 16:31:51 +08:00
Redkale
71b91e6896 2018-01-03 16:27:35 +08:00
Redkale
30d4296687 2018-01-03 16:26:10 +08:00
Redkale
7b0a576b00 2018-01-03 16:11:11 +08:00
Redkale
808a3c2e9c WebSocket模块broadcastMessage方法增加WebSocketRange过滤参数 2018-01-03 11:17:00 +08:00
Redkale
c700ffbadc 2018-01-02 16:58:49 +08:00
Redkale
e2057676bd 优化WebSocketNode.sendMessage 2018-01-02 16:42:51 +08:00
Redkale
f7dfb32849 Redkale 1.9.0 开始 2018-01-02 15:47:15 +08:00
Redkale
6413161dc8 net包优化 2017-12-29 10:19:20 +08:00
Redkale
46a89d9847 @RestHeader增加支持Host、Content-Type、Connection选项 2017-12-29 08:31:19 +08:00
Redkale
d83d6b5671 2017-12-29 08:29:27 +08:00
Redkale
b1abe91bd2 2017-12-28 21:05:20 +08:00
Redkale
bf4138178b 2017-12-28 21:04:55 +08:00
Redkale
5b99084616 2017-12-28 19:59:45 +08:00
Redkale
b95064b87e 2017-12-28 19:10:11 +08:00
Redkale
e1eece34b7 增加restart脚本 2017-12-28 11:28:47 +08:00
Redkale
19d6a06bfa 还原 2017-12-28 10:53:34 +08:00
Redkale
2fd13d1481 2017-12-28 10:40:25 +08:00
Redkale
23a0a80e3a Transport增加readTimeoutSecond、writeTimeoutSecond配置项 2017-12-28 10:24:15 +08:00
Redkale
bb493d43b5 RetResult的attach属性由Map<String, String> 改成 Map<String, Serializable> 2017-12-28 10:19:49 +08:00
Redkale
ef9eaa0a66 DataSource.updateColumn系列方法屏蔽掉limit功能 2017-12-28 10:16:21 +08:00
Redkale
1d640f943a CacheSource增加existsSetItem系列方法 2017-12-28 10:14:10 +08:00
Redkale
84f5f065ad Redkale 1.8.9 开始 2017-12-28 10:13:04 +08:00
168 changed files with 28446 additions and 4706 deletions

View File

@@ -1,6 +1,6 @@
<h1>项目介绍</h1>
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redkale (中文名: 红菜苔,湖北武汉的一种特产蔬菜) 是基于Java 8全新的微服务框架 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 8全新的微服务框架 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。
</p>
<strong>RedKale 有如下主要特点:</strong>
<ol>
@@ -19,7 +19,7 @@
</p>
&nbsp;&nbsp;&nbsp;由于RedKale使用了JDK 8 内置的ASM包所以需要在源码工程中的编译器选项中加入: <b>-XDignore.symbol.file=true</b>
&nbsp;&nbsp;&nbsp;编译RedKale 1.8.x版本需要在源码工程中的编译器选项中加入: <b>-XDignore.symbol.file=true</b>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>详情请访问:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org' target='_blank'>https://redkale.org</a></h5>

View File

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

9
bin/restart.bat Normal file
View File

@@ -0,0 +1,9 @@
@ECHO OFF
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\start.bat"

20
bin/restart.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
export LC_ALL="zh_CN.UTF-8"
APP_HOME=`dirname "$0"`
cd "$APP_HOME"/..
APP_HOME=`pwd`
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/..
fi
cd "$APP_HOME"
./bin/shutdown.sh
./bin/start.sh

View File

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

View File

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

View File

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

View File

1
my/readme.txt Normal file
View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>Ŀ¼<EFBFBD>µ<EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatypeʱʹ<EFBFBD>ã<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><DAB9>̴<EFBFBD><CCB4><EFBFBD><EFBFBD><EFBFBD>

View File

@@ -34,12 +34,14 @@
<!--
【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
threads 线程总数, 默认: <group>节点数*CPU核数*8
bufferCapacity: ByteBuffer的初始化大小 默认: 8K;
bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8
threads 线程总数, 默认: <group>节点数*CPU核数*2
bufferCapacity: ByteBuffer的初始化大小 默认: 32K;
bufferPoolSize ByteBuffer池的大小默认: 线程总数*4
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒 为0表示无超时限制
writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒 为0表示无超时限制
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
-->
<transport bufferCapacity="8K" bufferPoolSize="32" threads="32"/>
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
<!--
一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
@@ -55,8 +57,6 @@
同一个<node>节点值只能存在一个<group>节点内即同一个addr+port只能属于一个group。
addr: required IP地址
port: required 端口
clients: 连接池数, 默认: CPU核数*4
buffers: ByteBuffer对象池的大小 默认: CPU核数*8
-->
<node addr="127.0.0.1" port="7070"/>
</group>
@@ -64,7 +64,7 @@
<!--
全局的数据源设置, 可以是CacheSource、DataSource JDBC的DataSource通常通过persistence.xml配置此处多用于CacheSource的配置
name: 资源名,用于依赖注入。
value类名必须是CacheSource或DataSource的子类且必须实现Service接口。
value类名必须是CacheSource或DataSource的子类且必须实现Service接口。如果是DataSource.class系统自动映射成DataJdbcSource.class
groups: 指定groups。
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
-->
@@ -85,6 +85,9 @@
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
load: 加载文件,多个用;隔开。
默认置入的system.property.的有:
System.setProperty("net.transport.poolmaxconns", "100");
System.setProperty("net.transport.pinginterval", "30");
System.setProperty("net.transport.checkinterval", "30");
System.setProperty("convert.json.tiny", "true");
System.setProperty("convert.bson.tiny", "true");
System.setProperty("convert.json.pool.size", "128");
@@ -92,7 +95,8 @@
System.setProperty("convert.json.writer.buffer.defsize", "4096");
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
<properties>节点下也可包含非<property>节点,其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
<properties>节点下也可包含非<property>节点.
非<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
-->
<properties load="config.properties">
<property name="system.property.yyyy" value="YYYYYY"/>
@@ -112,18 +116,31 @@
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程数, 默认: CPU核数*16
threads 线程数, 默认: CPU核数*32
maxconns最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 默认: 8K; 如果是HTTP协议则默认: 16K + 16B (兼容HTTP 2.0、WebSocket)
bufferPoolSize ByteBuffer池的大小默认: CPU核数*512
responsePoolSize Response池的大小默认: CPU核数*256
readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSecond: 操作超时秒数, 默认0 表示永久不超时
bufferCapacity: ByteBuffer的初始化大小 默认: 32K; (HTTP 2.0、WebSocket必须要16k以上)
bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2
aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30 0表示永久不超时; -1表示禁止KeepAlive
readTimeoutSeconds: 操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值也可以是aio或nio默认值为aioUDP情况下值也可以是bio默认值为bio
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
-->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
<!--
【节点在<server>中唯一】
value: 创建SSLContext的实现类, 可自定义必须是org.redkale.net.SSLCreator的子类
clientauth: true/false/want
keystorepass: KEY密码
keystorefile: KEY文件
truststorepass: TRUST密码
truststorefile: TRUST文件
-->
<ssl creator=""/>
<!--
加载所有的Service服务;
在同一个进程中同一个name同一类型的Service将共用同一个实例
@@ -147,7 +164,7 @@
<service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/>
<!-- 给Service增加配置属性 -->
<service value="com.xxx.XXX1Service">
<!-- property节点值在 public void init(AnyValue conf) 方法中可以通过 AnyValue properties = conf.getAnyValue("properties");获取 -->
<!-- property值在public void init(AnyValue conf)方法中可以通过AnyValue properties=conf.getAnyValue("properties")获取 -->
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
</service>
@@ -172,7 +189,7 @@
<!-- 给Filter增加配置属性 -->
<filter value="com.xxx.XXX12Filter">
<!-- property节点值在 public void init(AnyValue conf) 方法中可以通过 AnyValue properties = conf.getAnyValue("properties");获取 -->
<!-- property值在public void init(AnyValue conf)方法中可以通过AnyValue properties=conf.getAnyValue("properties")获取 -->
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
</filter>
@@ -214,20 +231,33 @@
<!--
【节点在<server>中唯一】
当Server为HTTP协议时, response节点才有效。
contenttype: plain值为调用finish时的ContentType; 默认值: text/plain; charset=utf-8
json值为调用finishJson时的ContentType; 默认值: application/json; charset=utf-8
defcookie 节点: 当response里输出的cookie没有指定domain 和path时使用该节点的默认值。
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
例如下面例子是在Response输出header时添加两个header一个addHeader 一个setHeader
options 节点: 设置了该节点auto=true当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
options 节点: 设置了该节点auto=true当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
date 节点: 设置了该节点且period有值(单位:毫秒);返回response会包含Date头信息默认为period=0
period=0表示实时获取当前时间;
period<0表示不设置date;
period>0表示定时获取时间; 设置1000表示每秒刷新Date时间
-->
<response>
<contenttype plain="text/plain; charset=utf-8" json="application/json; charset=utf-8"/>
<defcookie domain="" path=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/>
<setheader name="Access-Control-Allow-Credentials" value="true"/>
<options auto="true" />
<date period="0" />
</response>
<!--
【节点在<server>中唯一】
当Server为HTTP协议时render才有效. 指定输出引擎的实现类
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
-->
<render value="org.redkalex.htel.HttpTemplateRender"/>
<!--
【节点在<server>中唯一】
当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点

View File

@@ -18,6 +18,8 @@ java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
#java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
#\u9700\u8981\u5c4f\u853d\u6d88\u606f\u5185\u5bb9\u7684\u6b63\u5219\u8868\u8fbe\u5f0f
java.util.logging.FileHandler.denyreg =
java.util.logging.FileHandler.append = true
#java.util.logging.ConsoleHandler.level = FINE

View File

@@ -6,11 +6,17 @@
*
module org.redkale {
requires java.se;
requires jdk.unsupported;
requires java.base;
requires java.logging;
requires java.xml;
requires java.sql;
requires java.sql.rowset;
requires jdk.unsupported; //sun.misc.Unsafe
exports javax.annotation;
exports javax.persistence;
exports org.redkale.boot;
exports org.redkale.boot.watch;
exports org.redkale.convert;

View File

@@ -0,0 +1,195 @@
/*
* 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 annotation. The methods of this class must be
* called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public abstract class AnnotationVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
protected final int api;
/**
* The annotation visitor to which this visitor must delegate method calls.
* May be null.
*/
protected AnnotationVisitor av;
/**
* Constructs a new {@link AnnotationVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
public AnnotationVisitor(final int api) {
this(api, null);
}
/**
* Constructs a new {@link AnnotationVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
* @param av
* the annotation visitor to which this visitor must delegate
* method calls. May be null.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
this.api = api;
this.av = av;
}
/**
* Visits a primitive value of the annotation.
*
* @param name
* the value name.
* @param value
* the actual value, whose type must be {@link Byte},
* {@link Boolean}, {@link Character}, {@link Short},
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
* {@link String} or {@link Type} of OBJECT or ARRAY sort. This
* value can also be an array of byte, boolean, short, char, int,
* long, float or double values (this is equivalent to using
* {@link #visitArray visitArray} and visiting each array element
* in turn, but is more convenient).
*/
public void visit(String name, Object value) {
if (av != null) {
av.visit(name, value);
}
}
/**
* Visits an enumeration value of the annotation.
*
* @param name
* the value name.
* @param desc
* the class descriptor of the enumeration class.
* @param value
* the actual enumeration value.
*/
public void visitEnum(String name, String desc, String value) {
if (av != null) {
av.visitEnum(name, desc, value);
}
}
/**
* Visits a nested annotation value of the annotation.
*
* @param name
* the value name.
* @param desc
* the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or
* <tt>null</tt> if this visitor is not interested in visiting this
* nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation
* visitor</i>.
*/
public AnnotationVisitor visitAnnotation(String name, String desc) {
if (av != null) {
return av.visitAnnotation(name, desc);
}
return null;
}
/**
* Visits an array value of the annotation. Note that arrays of primitive
* types (such as byte, boolean, short, char, int, long, float or double)
* can be passed as value to {@link #visit visit}. This is what
* {@link ClassReader} does.
*
* @param name
* the value name.
* @return a visitor to visit the actual array value elements, or
* <tt>null</tt> if this visitor is not interested in visiting these
* values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>.
*/
public AnnotationVisitor visitArray(String name) {
if (av != null) {
return av.visitArray(name);
}
return null;
}
/**
* Visits the end of the annotation.
*/
public void visitEnd() {
if (av != null) {
av.visitEnd();
}
}
}

View File

@@ -0,0 +1,400 @@
/*
* 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;
/**
* An {@link AnnotationVisitor} that generates annotations in bytecode form.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
final class AnnotationWriter extends AnnotationVisitor {
/**
* The class writer to which this annotation must be added.
*/
private final ClassWriter cw;
/**
* The number of values in this annotation.
*/
private int size;
/**
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
* writers used for annotation default and annotation arrays use unnamed
* values.
*/
private final boolean named;
/**
* The annotation values in bytecode form. This byte vector only contains
* the values themselves, i.e. the number of values must be stored as a
* unsigned short just before these bytes.
*/
private final ByteVector bv;
/**
* The byte vector to be used to store the number of values of this
* annotation. See {@link #bv}.
*/
private final ByteVector parent;
/**
* Where the number of values of this annotation must be stored in
* {@link #parent}.
*/
private final int offset;
/**
* Next annotation writer. This field is used to store annotation lists.
*/
AnnotationWriter next;
/**
* Previous annotation writer. This field is used to store annotation lists.
*/
AnnotationWriter prev;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new {@link AnnotationWriter}.
*
* @param cw
* the class writer to which this annotation must be added.
* @param named
* <tt>true<tt> if values are named, <tt>false</tt> otherwise.
* @param bv
* where the annotation values must be stored.
* @param parent
* where the number of annotation values must be stored.
* @param offset
* where in <tt>parent</tt> the number of annotation values must
* be stored.
*/
AnnotationWriter(final ClassWriter cw, final boolean named,
final ByteVector bv, final ByteVector parent, final int offset) {
super(Opcodes.ASM6);
this.cw = cw;
this.named = named;
this.bv = bv;
this.parent = parent;
this.offset = offset;
}
// ------------------------------------------------------------------------
// Implementation of the AnnotationVisitor abstract class
// ------------------------------------------------------------------------
@Override
public void visit(final String name, final Object value) {
++size;
if (named) {
bv.putShort(cw.newUTF8(name));
}
if (value instanceof String) {
bv.put12('s', cw.newUTF8((String) value));
} else if (value instanceof Byte) {
bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index);
} else if (value instanceof Boolean) {
int v = ((Boolean) value).booleanValue() ? 1 : 0;
bv.put12('Z', cw.newInteger(v).index);
} else if (value instanceof Character) {
bv.put12('C', cw.newInteger(((Character) value).charValue()).index);
} else if (value instanceof Short) {
bv.put12('S', cw.newInteger(((Short) value).shortValue()).index);
} else if (value instanceof Type) {
bv.put12('c', cw.newUTF8(((Type) value).getDescriptor()));
} else if (value instanceof byte[]) {
byte[] v = (byte[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('B', cw.newInteger(v[i]).index);
}
} else if (value instanceof boolean[]) {
boolean[] v = (boolean[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index);
}
} else if (value instanceof short[]) {
short[] v = (short[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('S', cw.newInteger(v[i]).index);
}
} else if (value instanceof char[]) {
char[] v = (char[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('C', cw.newInteger(v[i]).index);
}
} else if (value instanceof int[]) {
int[] v = (int[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('I', cw.newInteger(v[i]).index);
}
} else if (value instanceof long[]) {
long[] v = (long[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('J', cw.newLong(v[i]).index);
}
} else if (value instanceof float[]) {
float[] v = (float[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('F', cw.newFloat(v[i]).index);
}
} else if (value instanceof double[]) {
double[] v = (double[]) value;
bv.put12('[', v.length);
for (int i = 0; i < v.length; i++) {
bv.put12('D', cw.newDouble(v[i]).index);
}
} else {
Item i = cw.newConstItem(value);
bv.put12(".s.IFJDCS".charAt(i.type), i.index);
}
}
@Override
public void visitEnum(final String name, final String desc,
final String value) {
++size;
if (named) {
bv.putShort(cw.newUTF8(name));
}
bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value));
}
@Override
public AnnotationVisitor visitAnnotation(final String name,
final String desc) {
++size;
if (named) {
bv.putShort(cw.newUTF8(name));
}
// write tag and type, and reserve space for values count
bv.put12('@', cw.newUTF8(desc)).putShort(0);
return new AnnotationWriter(cw, true, bv, bv, bv.length - 2);
}
@Override
public AnnotationVisitor visitArray(final String name) {
++size;
if (named) {
bv.putShort(cw.newUTF8(name));
}
// write tag, and reserve space for array size
bv.put12('[', 0);
return new AnnotationWriter(cw, false, bv, bv, bv.length - 2);
}
@Override
public void visitEnd() {
if (parent != null) {
byte[] data = parent.data;
data[offset] = (byte) (size >>> 8);
data[offset + 1] = (byte) size;
}
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Returns the size of this annotation writer list.
*
* @return the size of this annotation writer list.
*/
int getSize() {
int size = 0;
AnnotationWriter aw = this;
while (aw != null) {
size += aw.bv.length;
aw = aw.next;
}
return size;
}
/**
* Puts the annotations of this annotation writer list into the given byte
* vector.
*
* @param out
* where the annotations must be put.
*/
void put(final ByteVector out) {
int n = 0;
int size = 2;
AnnotationWriter aw = this;
AnnotationWriter last = null;
while (aw != null) {
++n;
size += aw.bv.length;
aw.visitEnd(); // in case user forgot to call visitEnd
aw.prev = last;
last = aw;
aw = aw.next;
}
out.putInt(size);
out.putShort(n);
aw = last;
while (aw != null) {
out.putByteArray(aw.bv.data, 0, aw.bv.length);
aw = aw.prev;
}
}
/**
* Puts the given annotation lists into the given byte vector.
*
* @param panns
* an array of annotation writer lists.
* @param off
* index of the first annotation to be written.
* @param out
* where the annotations must be put.
*/
static void put(final AnnotationWriter[] panns, final int off,
final ByteVector out) {
int size = 1 + 2 * (panns.length - off);
for (int i = off; i < panns.length; ++i) {
size += panns[i] == null ? 0 : panns[i].getSize();
}
out.putInt(size).putByte(panns.length - off);
for (int i = off; i < panns.length; ++i) {
AnnotationWriter aw = panns[i];
AnnotationWriter last = null;
int n = 0;
while (aw != null) {
++n;
aw.visitEnd(); // in case user forgot to call visitEnd
aw.prev = last;
last = aw;
aw = aw.next;
}
out.putShort(n);
aw = last;
while (aw != null) {
out.putByteArray(aw.bv.data, 0, aw.bv.length);
aw = aw.prev;
}
}
}
/**
* Puts the given type reference and type path into the given bytevector.
* LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported.
*
* @param typeRef
* a reference to the annotated type. See {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param out
* where the type reference and type path must be put.
*/
static void putTarget(int typeRef, TypePath typePath, ByteVector out) {
switch (typeRef >>> 24) {
case 0x00: // CLASS_TYPE_PARAMETER
case 0x01: // METHOD_TYPE_PARAMETER
case 0x16: // METHOD_FORMAL_PARAMETER
out.putShort(typeRef >>> 16);
break;
case 0x13: // FIELD
case 0x14: // METHOD_RETURN
case 0x15: // METHOD_RECEIVER
out.putByte(typeRef >>> 24);
break;
case 0x47: // CAST
case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
out.putInt(typeRef);
break;
// case 0x10: // CLASS_EXTENDS
// case 0x11: // CLASS_TYPE_PARAMETER_BOUND
// case 0x12: // METHOD_TYPE_PARAMETER_BOUND
// case 0x17: // THROWS
// case 0x42: // EXCEPTION_PARAMETER
// case 0x43: // INSTANCEOF
// case 0x44: // NEW
// case 0x45: // CONSTRUCTOR_REFERENCE
// case 0x46: // METHOD_REFERENCE
default:
out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8);
break;
}
if (typePath == null) {
out.putByte(0);
} else {
int length = typePath.b[typePath.offset] * 2 + 1;
out.putByteArray(typePath.b, typePath.offset, length);
}
}
}

View File

@@ -0,0 +1,284 @@
/*
* 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 non standard class, field, method or code attribute.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
class Attribute {
/**
* The type of this attribute.
*/
public final String type;
/**
* The raw value of this attribute, used only for unknown attributes.
*/
byte[] value;
/**
* The next attribute in this attribute list. May be <tt>null</tt>.
*/
Attribute next;
/**
* Constructs a new empty attribute.
*
* @param type
* the type of the attribute.
*/
protected Attribute(final String type) {
this.type = type;
}
/**
* Returns <tt>true</tt> if this type of attribute is unknown. The default
* implementation of this method always returns <tt>true</tt>.
*
* @return <tt>true</tt> if this type of attribute is unknown.
*/
public boolean isUnknown() {
return true;
}
/**
* Returns <tt>true</tt> if this type of attribute is a code attribute.
*
* @return <tt>true</tt> if this type of attribute is a code attribute.
*/
public boolean isCodeAttribute() {
return false;
}
/**
* Returns the labels corresponding to this attribute.
*
* @return the labels corresponding to this attribute, or <tt>null</tt> if
* this attribute is not a code attribute that contains labels.
*/
protected Label[] getLabels() {
return null;
}
/**
* Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type},
* corresponding to the <tt>len</tt> bytes starting at the given offset, in
* the given class reader.
*
* @param cr
* the class that contains the attribute to be read.
* @param off
* index of the first byte of the attribute's content in
* {@link ClassReader#b cr.b}. The 6 attribute header bytes,
* containing the type and the length of the attribute, are not
* taken into account here.
* @param len
* the length of the attribute's content.
* @param buf
* buffer to be used to call {@link ClassReader#readUTF8
* readUTF8}, {@link ClassReader#readClass(int,char[]) readClass}
* or {@link ClassReader#readConst readConst}.
* @param codeOff
* index of the first byte of code's attribute content in
* {@link ClassReader#b cr.b}, or -1 if the attribute to be read
* is not a code attribute. The 6 attribute header bytes,
* containing the type and the length of the attribute, are not
* taken into account here.
* @param labels
* the labels of the method's code, or <tt>null</tt> if the
* attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes.
*/
protected Attribute read(final ClassReader cr, final int off,
final int len, final char[] buf, final int codeOff,
final Label[] labels) {
Attribute attr = new Attribute(type);
attr.value = new byte[len];
System.arraycopy(cr.b, off, attr.value, 0, len);
return attr;
}
/**
* Returns the byte array form of this attribute.
*
* @param cw
* the class to which this attribute must be added. This
* parameter can be used to add to the constant pool of this
* class the items that corresponds to this attribute.
* @param code
* the bytecode of the method corresponding to this code
* attribute, or <tt>null</tt> if this attribute is not a code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to this
* code attribute, or <tt>null</tt> if this attribute is not a
* code attribute.
* @param maxStack
* the maximum stack size of the method corresponding to this
* code attribute, or -1 if this attribute is not a code
* attribute.
* @param maxLocals
* the maximum number of local variables of the method
* corresponding to this code attribute, or -1 if this attribute
* is not a code attribute.
* @return the byte array form of this attribute.
*/
protected ByteVector write(final ClassWriter cw, final byte[] code,
final int len, final int maxStack, final int maxLocals) {
ByteVector v = new ByteVector();
v.data = value;
v.length = value.length;
return v;
}
/**
* Returns the length of the attribute list that begins with this attribute.
*
* @return the length of the attribute list that begins with this attribute.
*/
final int getCount() {
int count = 0;
Attribute attr = this;
while (attr != null) {
count += 1;
attr = attr.next;
}
return count;
}
/**
* Returns the size of all the attributes in this attribute list.
*
* @param cw
* the class writer to be used to convert the attributes into
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <tt>null</tt> if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <tt>null</tt> if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
* code attributes, or -1 if these attributes are not code
* attributes.
* @param maxLocals
* the maximum number of local variables of the method
* corresponding to these code attributes, or -1 if these
* attributes are not code attributes.
* @return the size of all the attributes in this attribute list. This size
* includes the size of the attribute headers.
*/
final int getSize(final ClassWriter cw, final byte[] code, final int len,
final int maxStack, final int maxLocals) {
Attribute attr = this;
int size = 0;
while (attr != null) {
cw.newUTF8(attr.type);
size += attr.write(cw, code, len, maxStack, maxLocals).length + 6;
attr = attr.next;
}
return size;
}
/**
* Writes all the attributes of this attribute list in the given byte
* vector.
*
* @param cw
* the class writer to be used to convert the attributes into
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <tt>null</tt> if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <tt>null</tt> if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
* code attributes, or -1 if these attributes are not code
* attributes.
* @param maxLocals
* the maximum number of local variables of the method
* corresponding to these code attributes, or -1 if these
* attributes are not code attributes.
* @param out
* where the attributes must be written.
*/
final void put(final ClassWriter cw, final byte[] code, final int len,
final int maxStack, final int maxLocals, final ByteVector out) {
Attribute attr = this;
while (attr != null) {
ByteVector b = attr.write(cw, code, len, maxStack, maxLocals);
out.putShort(cw.newUTF8(attr.type)).putInt(b.length);
out.putByteArray(b.data, 0, b.length);
attr = attr.next;
}
}
}

View File

@@ -0,0 +1,368 @@
/*
* 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 dynamically extensible vector of bytes. This class is roughly equivalent to
* a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
*
* @author Eric Bruneton
*/
public class ByteVector {
/**
* The content of this vector.
*/
byte[] data;
/**
* Actual number of bytes in this vector.
*/
int length;
/**
* Constructs a new {@link ByteVector ByteVector} with a default initial
* size.
*/
public ByteVector() {
data = new byte[64];
}
/**
* Constructs a new {@link ByteVector ByteVector} with the given initial
* size.
*
* @param initialSize
* the initial size of the byte vector to be constructed.
*/
public ByteVector(final int initialSize) {
data = new byte[initialSize];
}
/**
* Puts a byte into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param b
* a byte.
* @return this byte vector.
*/
public ByteVector putByte(final int b) {
int length = this.length;
if (length + 1 > data.length) {
enlarge(1);
}
data[length++] = (byte) b;
this.length = length;
return this;
}
/**
* Puts two bytes into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param b1
* a byte.
* @param b2
* another byte.
* @return this byte vector.
*/
ByteVector put11(final int b1, final int b2) {
int length = this.length;
if (length + 2 > data.length) {
enlarge(2);
}
byte[] data = this.data;
data[length++] = (byte) b1;
data[length++] = (byte) b2;
this.length = length;
return this;
}
/**
* Puts a short into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param s
* a short.
* @return this byte vector.
*/
public ByteVector putShort(final int s) {
int length = this.length;
if (length + 2 > data.length) {
enlarge(2);
}
byte[] data = this.data;
data[length++] = (byte) (s >>> 8);
data[length++] = (byte) s;
this.length = length;
return this;
}
/**
* Puts a byte and a short into this byte vector. The byte vector is
* automatically enlarged if necessary.
*
* @param b
* a byte.
* @param s
* a short.
* @return this byte vector.
*/
ByteVector put12(final int b, final int s) {
int length = this.length;
if (length + 3 > data.length) {
enlarge(3);
}
byte[] data = this.data;
data[length++] = (byte) b;
data[length++] = (byte) (s >>> 8);
data[length++] = (byte) s;
this.length = length;
return this;
}
/**
* Puts an int into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param i
* an int.
* @return this byte vector.
*/
public ByteVector putInt(final int i) {
int length = this.length;
if (length + 4 > data.length) {
enlarge(4);
}
byte[] data = this.data;
data[length++] = (byte) (i >>> 24);
data[length++] = (byte) (i >>> 16);
data[length++] = (byte) (i >>> 8);
data[length++] = (byte) i;
this.length = length;
return this;
}
/**
* Puts a long into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param l
* a long.
* @return this byte vector.
*/
public ByteVector putLong(final long l) {
int length = this.length;
if (length + 8 > data.length) {
enlarge(8);
}
byte[] data = this.data;
int i = (int) (l >>> 32);
data[length++] = (byte) (i >>> 24);
data[length++] = (byte) (i >>> 16);
data[length++] = (byte) (i >>> 8);
data[length++] = (byte) i;
i = (int) l;
data[length++] = (byte) (i >>> 24);
data[length++] = (byte) (i >>> 16);
data[length++] = (byte) (i >>> 8);
data[length++] = (byte) i;
this.length = length;
return this;
}
/**
* Puts an UTF8 string into this byte vector. The byte vector is
* automatically enlarged if necessary.
*
* @param s
* a String whose UTF8 encoded length must be less than 65536.
* @return this byte vector.
*/
public ByteVector putUTF8(final String s) {
int charLength = s.length();
if (charLength > 65535) {
throw new IllegalArgumentException();
}
int len = length;
if (len + 2 + charLength > data.length) {
enlarge(2 + charLength);
}
byte[] data = this.data;
// optimistic algorithm: instead of computing the byte length and then
// serializing the string (which requires two loops), we assume the byte
// length is equal to char length (which is the most frequent case), and
// we start serializing the string right away. During the serialization,
// if we find that this assumption is wrong, we continue with the
// general method.
data[len++] = (byte) (charLength >>> 8);
data[len++] = (byte) charLength;
for (int i = 0; i < charLength; ++i) {
char c = s.charAt(i);
if (c >= '\001' && c <= '\177') {
data[len++] = (byte) c;
} else {
length = len;
return encodeUTF8(s, i, 65535);
}
}
length = len;
return this;
}
/**
* Puts an UTF8 string into this byte vector. The byte vector is
* automatically enlarged if necessary. The string length is encoded in two
* bytes before the encoded characters, if there is space for that (i.e. if
* this.length - i - 2 >= 0).
*
* @param s
* the String to encode.
* @param i
* the index of the first character to encode. The previous
* characters are supposed to have already been encoded, using
* only one byte per character.
* @param maxByteLength
* the maximum byte length of the encoded string, including the
* already encoded characters.
* @return this byte vector.
*/
ByteVector encodeUTF8(final String s, int i, int maxByteLength) {
int charLength = s.length();
int byteLength = i;
char c;
for (int j = i; j < charLength; ++j) {
c = s.charAt(j);
if (c >= '\001' && c <= '\177') {
byteLength++;
} else if (c > '\u07FF') {
byteLength += 3;
} else {
byteLength += 2;
}
}
if (byteLength > maxByteLength) {
throw new IllegalArgumentException();
}
int start = length - i - 2;
if (start >= 0) {
data[start] = (byte) (byteLength >>> 8);
data[start + 1] = (byte) byteLength;
}
if (length + byteLength - i > data.length) {
enlarge(byteLength - i);
}
int len = length;
for (int j = i; j < charLength; ++j) {
c = s.charAt(j);
if (c >= '\001' && c <= '\177') {
data[len++] = (byte) c;
} else if (c > '\u07FF') {
data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
data[len++] = (byte) (0x80 | c & 0x3F);
} else {
data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
data[len++] = (byte) (0x80 | c & 0x3F);
}
}
length = len;
return this;
}
/**
* Puts an array of bytes into this byte vector. The byte vector is
* automatically enlarged if necessary.
*
* @param b
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
* null bytes into this byte vector.
* @param off
* index of the fist byte of b that must be copied.
* @param len
* number of bytes of b that must be copied.
* @return this byte vector.
*/
public ByteVector putByteArray(final byte[] b, final int off, final int len) {
if (length + len > data.length) {
enlarge(len);
}
if (b != null) {
System.arraycopy(b, off, data, length, len);
}
length += len;
return this;
}
/**
* Enlarge this byte vector so that it can receive n more bytes.
*
* @param size
* number of additional bytes that this byte vector should be
* able to receive.
*/
private void enlarge(final int size) {
int length1 = 2 * data.length;
int length2 = length + size;
byte[] newData = new byte[length1 > length2 ? length1 : length2];
System.arraycopy(data, 0, newData, 0, length);
data = newData;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,365 @@
/*
* 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 class. The methods of this class must be called in
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
* <tt>visitEnd</tt>.
*
* @author Eric Bruneton
*/
public abstract class ClassVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
protected final int api;
/**
* The class visitor to which this visitor must delegate method calls. May
* be null.
*/
protected ClassVisitor cv;
/**
* Constructs a new {@link ClassVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
public ClassVisitor(final int api) {
this(api, null);
}
/**
* Constructs a new {@link ClassVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
* @param cv
* the class visitor to which this visitor must delegate method
* calls. May be null.
*/
public ClassVisitor(final int api, final ClassVisitor cv) {
this.api = api;
this.cv = cv;
}
/**
* Visits the header of the class.
*
* @param version
* the class version.
* @param access
* the class's access flags (see {@link Opcodes}). This parameter
* also indicates if the class is deprecated.
* @param name
* the internal name of the class (see
* {@link Type#getInternalName() getInternalName}).
* @param signature
* the signature of this class. May be <tt>null</tt> if the class
* is not a generic one, and does not extend or implement generic
* classes or interfaces.
* @param superName
* the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be
* <tt>null</tt>, but only for the {@link Object} class.
* @param interfaces
* the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>.
*/
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
if (cv != null) {
cv.visit(version, access, name, signature, superName, interfaces);
}
}
/**
* Visits the source of the class.
*
* @param source
* the name of the source file from which the class was compiled.
* May be <tt>null</tt>.
* @param debug
* additional debug information to compute the correspondance
* between source and compiled elements of the class. May be
* <tt>null</tt>.
*/
public void visitSource(String source, String debug) {
if (cv != null) {
cv.visitSource(source, debug);
}
}
/**
* Visit the module corresponding to the class.
* @param name
* module name
* @param access
* module flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC}
* and {@code ACC_MANDATED}.
* @param version
* module version or null.
* @return a visitor to visit the module values, or <tt>null</tt> if
* this visitor is not interested in visiting this module.
*/
public ModuleVisitor visitModule(String name, int access, String version) {
if (api < Opcodes.ASM6) {
throw new RuntimeException();
}
if (cv != null) {
return cv.visitModule(name, access, version);
}
return null;
}
/**
* Visits the enclosing class of the class. This method must be called only
* if the class has an enclosing class.
*
* @param owner
* internal name of the enclosing class of the class.
* @param name
* the name of the method that contains the class, or
* <tt>null</tt> if the class is not enclosed in a method of its
* enclosing class.
* @param desc
* the descriptor of the method that contains the class, or
* <tt>null</tt> if the class is not enclosed in a method of its
* enclosing class.
*/
public void visitOuterClass(String owner, String name, String desc) {
if (cv != null) {
cv.visitOuterClass(owner, name, desc);
}
}
/**
* Visits an annotation of the class.
*
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (cv != null) {
return cv.visitAnnotation(desc, visible);
}
return null;
}
/**
* Visits an annotation on a type in the class signature.
*
* @param typeRef
* a reference to the annotated type. The sort of this type
* reference must be {@link TypeReference#CLASS_TYPE_PARAMETER
* CLASS_TYPE_PARAMETER},
* {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND
* CLASS_TYPE_PARAMETER_BOUND} or
* {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See
* {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
return null;
}
/**
* Visits a non standard attribute of the class.
*
* @param attr
* an attribute.
*/
public void visitAttribute(Attribute attr) {
if (cv != null) {
cv.visitAttribute(attr);
}
}
/**
* Visits information about an inner class. This inner class is not
* necessarily a member of the class being visited.
*
* @param name
* the internal name of an inner class (see
* {@link Type#getInternalName() getInternalName}).
* @param outerName
* the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}).
* May be <tt>null</tt> for not member classes.
* @param innerName
* the (simple) name of the inner class inside its enclosing
* class. May be <tt>null</tt> for anonymous inner classes.
* @param access
* the access flags of the inner class as originally declared in
* the enclosing class.
*/
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
if (cv != null) {
cv.visitInnerClass(name, outerName, innerName, access);
}
}
/**
* Visits a field of the class.
*
* @param access
* the field's access flags (see {@link Opcodes}). This parameter
* also indicates if the field is synthetic and/or deprecated.
* @param name
* the field's name.
* @param desc
* the field's descriptor (see {@link Type Type}).
* @param signature
* the field's signature. May be <tt>null</tt> if the field's
* type does not use generic types.
* @param value
* the field's initial value. This parameter, which may be
* <tt>null</tt> if the field does not have an initial value,
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <tt>int</tt>,
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
* respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in
* constructors or methods.
* @return a visitor to visit field annotations and attributes, or
* <tt>null</tt> if this class visitor is not interested in visiting
* these annotations and attributes.
*/
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
if (cv != null) {
return cv.visitField(access, name, desc, signature, value);
}
return null;
}
/**
* Visits a method of the class. This method <i>must</i> return a new
* {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
* i.e., it should not return a previously returned visitor.
*
* @param access
* the method's access flags (see {@link Opcodes}). This
* parameter also indicates if the method is synthetic and/or
* deprecated.
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param signature
* the method's signature. May be <tt>null</tt> if the method
* parameters, return type and exceptions do not use generic
* types.
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>.
* @return an object to visit the byte code of the method, or <tt>null</tt>
* if this class visitor is not interested in visiting the code of
* this method.
*/
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if (cv != null) {
return cv.visitMethod(access, name, desc, signature, exceptions);
}
return null;
}
/**
* Visits the end of the class. This method, which is the last one to be
* called, is used to inform the visitor that all the fields and methods of
* the class have been visited.
*/
public void visitEnd() {
if (cv != null) {
cv.visitEnd();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
/*
* 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 a class being parsed in a {@link ClassReader}.
*
* @author Eric Bruneton
*/
class Context {
/**
* Prototypes of the attributes that must be parsed for this class.
*/
Attribute[] attrs;
/**
* The {@link ClassReader} option flags for the parsing of this class.
*/
int flags;
/**
* The buffer used to read strings.
*/
char[] buffer;
/**
* The start index of each bootstrap method.
*/
int[] bootstrapMethods;
/**
* The access flags of the method currently being parsed.
*/
int access;
/**
* The name of the method currently being parsed.
*/
String name;
/**
* The descriptor of the method currently being parsed.
*/
String desc;
/**
* The label objects, indexed by bytecode offset, of the method currently
* being parsed (only bytecode offsets for which a label is needed have a
* non null associated Label object).
*/
Label[] labels;
/**
* The target of the type annotation currently being parsed.
*/
int typeRef;
/**
* The path of the type annotation currently being parsed.
*/
TypePath typePath;
/**
* The offset of the latest stack map frame that has been parsed.
*/
int offset;
/**
* The labels corresponding to the start of the local variable ranges in the
* local variable type annotation currently being parsed.
*/
Label[] start;
/**
* The labels corresponding to the end of the local variable ranges in the
* local variable type annotation currently being parsed.
*/
Label[] end;
/**
* The local variable indices for each local variable range in the local
* variable type annotation currently being parsed.
*/
int[] index;
/**
* The encoding of the latest stack map frame that has been parsed.
*/
int mode;
/**
* The number of locals in the latest stack map frame that has been parsed.
*/
int localCount;
/**
* The number locals in the latest stack map frame that has been parsed,
* minus the number of locals in the previous frame.
*/
int localDiff;
/**
* The local values of the latest stack map frame that has been parsed.
*/
Object[] local;
/**
* The stack size of the latest stack map frame that has been parsed.
*/
int stackCount;
/**
* The stack values of the latest stack map frame that has been parsed.
*/
Object[] stack;
}

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

@@ -0,0 +1,104 @@
/*
* 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;
/**
* An edge in the control flow graph of a method body. See {@link Label Label}.
*
* @author Eric Bruneton
*/
class Edge {
/**
* Denotes a normal control flow graph edge.
*/
static final int NORMAL = 0;
/**
* Denotes a control flow graph edge corresponding to an exception handler.
* More precisely any {@link Edge} whose {@link #info} is strictly positive
* corresponds to an exception handler. The actual value of {@link #info} is
* the index, in the {@link ClassWriter} type table, of the exception that
* is catched.
*/
static final int EXCEPTION = 0x7FFFFFFF;
/**
* Information about this control flow graph edge. If
* {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative)
* stack size in the basic block from which this edge originates. This size
* is equal to the stack size at the "jump" instruction to which this edge
* corresponds, relatively to the stack size at the beginning of the
* originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used,
* this field is the kind of this control flow graph edge (i.e. NORMAL or
* EXCEPTION).
*/
int info;
/**
* The successor block of the basic block from which this edge originates.
*/
Label successor;
/**
* The next edge in the list of successors of the originating basic block.
* See {@link Label#successors successors}.
*/
Edge next;
}

View File

@@ -0,0 +1,173 @@
/*
* 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 field. The methods of this class must be called in
* the following order: ( <tt>visitAnnotation</tt> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
*
* @author Eric Bruneton
*/
public abstract class FieldVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
protected final int api;
/**
* The field visitor to which this visitor must delegate method calls. May
* be null.
*/
protected FieldVisitor fv;
/**
* Constructs a new {@link FieldVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
public FieldVisitor(final int api) {
this(api, null);
}
/**
* Constructs a new {@link FieldVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
* @param fv
* the field visitor to which this visitor must delegate method
* calls. May be null.
*/
public FieldVisitor(final int api, final FieldVisitor fv) {
this.api = api;
this.fv = fv;
}
/**
* Visits an annotation of the field.
*
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (fv != null) {
return fv.visitAnnotation(desc, visible);
}
return null;
}
/**
* Visits an annotation on the type of the field.
*
* @param typeRef
* a reference to the annotated type. The sort of this type
* reference must be {@link TypeReference#FIELD FIELD}. See
* {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (fv != null) {
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
return null;
}
/**
* Visits a non standard attribute of the field.
*
* @param attr
* an attribute.
*/
public void visitAttribute(Attribute attr) {
if (fv != null) {
fv.visitAttribute(attr);
}
}
/**
* Visits the end of the field. This method, which is the last one to be
* called, is used to inform the visitor that all the annotations and
* attributes of the field have been visited.
*/
public void visitEnd() {
if (fv != null) {
fv.visitEnd();
}
}
}

View File

@@ -0,0 +1,352 @@
/*
* 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;
/**
* An {@link FieldVisitor} that generates Java fields in bytecode form.
*
* @author Eric Bruneton
*/
final class FieldWriter extends FieldVisitor {
/**
* The class writer to which this field must be added.
*/
private final ClassWriter cw;
/**
* Access flags of this field.
*/
private final int access;
/**
* The index of the constant pool item that contains the name of this
* method.
*/
private final int name;
/**
* The index of the constant pool item that contains the descriptor of this
* field.
*/
private final int desc;
/**
* The index of the constant pool item that contains the signature of this
* field.
*/
private int signature;
/**
* The index of the constant pool item that contains the constant value of
* this field.
*/
private int value;
/**
* The runtime visible annotations of this field. May be <tt>null</tt>.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this field. May be <tt>null</tt>.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this field. May be <tt>null</tt>.
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this field. May be
* <tt>null</tt>.
*/
private AnnotationWriter itanns;
/**
* The non standard attributes of this field. May be <tt>null</tt>.
*/
private Attribute attrs;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new {@link FieldWriter}.
*
* @param cw
* the class writer to which this field must be added.
* @param access
* the field's access flags (see {@link Opcodes}).
* @param name
* the field's name.
* @param desc
* the field's descriptor (see {@link Type}).
* @param signature
* the field's signature. May be <tt>null</tt>.
* @param value
* the field's constant value. May be <tt>null</tt>.
*/
FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) {
super(Opcodes.ASM6);
if (cw.firstField == null) {
cw.firstField = this;
} else {
cw.lastField.fv = this;
}
cw.lastField = this;
this.cw = cw;
this.access = access;
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
if (signature != null) {
this.signature = cw.newUTF8(signature);
}
if (value != null) {
this.value = cw.newConstItem(value).index;
}
}
// ------------------------------------------------------------------------
// Implementation of the FieldVisitor abstract class
// ------------------------------------------------------------------------
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
ByteVector bv = new ByteVector();
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
if (visible) {
aw.next = anns;
anns = aw;
} else {
aw.next = ianns;
ianns = aw;
}
return aw;
}
@Override
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
final TypePath typePath, final String desc, final boolean visible) {
ByteVector bv = new ByteVector();
// write target_type and target_info
AnnotationWriter.putTarget(typeRef, typePath, bv);
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
bv.length - 2);
if (visible) {
aw.next = tanns;
tanns = aw;
} else {
aw.next = itanns;
itanns = aw;
}
return aw;
}
@Override
public void visitAttribute(final Attribute attr) {
attr.next = attrs;
attrs = attr;
}
@Override
public void visitEnd() {
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Returns the size of this field.
*
* @return the size of this field.
*/
int getSize() {
int size = 8;
if (value != 0) {
cw.newUTF8("ConstantValue");
size += 8;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
cw.newUTF8("Synthetic");
size += 6;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
cw.newUTF8("Deprecated");
size += 6;
}
if (signature != 0) {
cw.newUTF8("Signature");
size += 8;
}
if (anns != null) {
cw.newUTF8("RuntimeVisibleAnnotations");
size += 8 + anns.getSize();
}
if (ianns != null) {
cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize();
}
if (tanns != null) {
cw.newUTF8("RuntimeVisibleTypeAnnotations");
size += 8 + tanns.getSize();
}
if (itanns != null) {
cw.newUTF8("RuntimeInvisibleTypeAnnotations");
size += 8 + itanns.getSize();
}
if (attrs != null) {
size += attrs.getSize(cw, null, 0, -1, -1);
}
return size;
}
/**
* Puts the content of this field into the given byte vector.
*
* @param out
* where the content of this field must be put.
*/
void put(final ByteVector out) {
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
out.putShort(access & ~mask).putShort(name).putShort(desc);
int attributeCount = 0;
if (value != 0) {
++attributeCount;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
++attributeCount;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
++attributeCount;
}
if (signature != 0) {
++attributeCount;
}
if (anns != null) {
++attributeCount;
}
if (ianns != null) {
++attributeCount;
}
if (tanns != null) {
++attributeCount;
}
if (itanns != null) {
++attributeCount;
}
if (attrs != null) {
attributeCount += attrs.getCount();
}
out.putShort(attributeCount);
if (value != 0) {
out.putShort(cw.newUTF8("ConstantValue"));
out.putInt(2).putShort(value);
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
out.putShort(cw.newUTF8("Synthetic")).putInt(0);
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
}
if (signature != 0) {
out.putShort(cw.newUTF8("Signature"));
out.putInt(2).putShort(signature);
}
if (anns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
anns.put(out);
}
if (ianns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
if (tanns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
tanns.put(out);
}
if (itanns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
itanns.put(out);
}
if (attrs != null) {
attrs.put(cw, null, 0, -1, -1, out);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
/*
* 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 reference to a field or a method.
*
* @author Remi Forax
* @author Eric Bruneton
*/
public final class Handle {
/**
* The kind of field or method designated by this Handle. Should 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}.
*/
final int tag;
/**
* The internal name of the class that owns the field or method designated
* by this handle.
*/
final String owner;
/**
* The name of the field or method designated by this handle.
*/
final String name;
/**
* The descriptor of the field or method designated by this handle.
*/
final String desc;
/**
* Indicate if the owner is an interface or not.
*/
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.
* @param itf
* true if the owner is an interface.
*/
public Handle(int tag, String owner, String name, String desc, boolean itf) {
this.tag = tag;
this.owner = owner;
this.name = name;
this.desc = desc;
this.itf = itf;
}
/**
* Returns the kind of field or method designated by this handle.
*
* @return {@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}.
*/
public int getTag() {
return tag;
}
/**
* Returns the internal name of the class that owns the field or method
* designated by this handle.
*
* @return the internal name of the class that owns the field or method
* designated by this handle.
*/
public String getOwner() {
return owner;
}
/**
* Returns the name of the field or method designated by this handle.
*
* @return the name of the field or method designated by this handle.
*/
public String getName() {
return name;
}
/**
* Returns the descriptor of the field or method designated by this handle.
*
* @return the descriptor of the field or method designated by this handle.
*/
public String getDesc() {
return desc;
}
/**
* Returns true if the owner of the field or method designated
* by this handle is an interface.
*
* @return true if the owner of the field or method designated
* by this handle is an interface.
*/
public boolean isInterface() {
return itf;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Handle)) {
return false;
}
Handle h = (Handle) obj;
return tag == h.tag && itf == h.itf && owner.equals(h.owner)
&& name.equals(h.name) && desc.equals(h.desc);
}
@Override
public int hashCode() {
return tag + (itf? 64: 0) + owner.hashCode() * name.hashCode() * desc.hashCode();
}
/**
* Returns the textual representation of this handle. The textual
* representation is:
*
* <pre>
* for a reference to a class:
* owner '.' name desc ' ' '(' tag ')'
* for a reference to an interface:
* owner '.' name desc ' ' '(' tag ' ' itf ')'
* </pre>
*
* . As this format is unambiguous, it can be parsed if necessary.
*/
@Override
public String toString() {
return owner + '.' + name + desc + " (" + tag + (itf? " itf": "") + ')';
}
}

View File

@@ -0,0 +1,150 @@
/*
* 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 an exception handler block.
*
* @author Eric Bruneton
*/
class Handler {
/**
* Beginning of the exception handler's scope (inclusive).
*/
Label start;
/**
* End of the exception handler's scope (exclusive).
*/
Label end;
/**
* Beginning of the exception handler's code.
*/
Label handler;
/**
* Internal name of the type of exceptions handled by this handler, or
* <tt>null</tt> to catch any exceptions.
*/
String desc;
/**
* Constant pool index of the internal name of the type of exceptions
* handled by this handler, or 0 to catch any exceptions.
*/
int type;
/**
* Next exception handler block info.
*/
Handler next;
/**
* Removes the range between start and end from the given exception
* handlers.
*
* @param h
* an exception handler list.
* @param start
* the start of the range to be removed.
* @param end
* the end of the range to be removed. Maybe null.
* @return the exception handler list with the start-end range removed.
*/
static Handler remove(Handler h, Label start, Label end) {
if (h == null) {
return null;
} else {
h.next = remove(h.next, start, end);
}
int hstart = h.start.position;
int hend = h.end.position;
int s = start.position;
int e = end == null ? Integer.MAX_VALUE : end.position;
// if [hstart,hend[ and [s,e[ intervals intersect...
if (s < hend && e > hstart) {
if (s <= hstart) {
if (e >= hend) {
// [hstart,hend[ fully included in [s,e[, h removed
h = h.next;
} else {
// [hstart,hend[ minus [s,e[ = [e,hend[
h.start = end;
}
} else if (e >= hend) {
// [hstart,hend[ minus [s,e[ = [hstart,s[
h.end = start;
} else {
// [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[
Handler g = new Handler();
g.start = end;
g.end = h.end;
g.handler = h.handler;
g.desc = h.desc;
g.type = h.type;
g.next = h.next;
h.end = start;
h.next = g;
}
}
return h;
}
}

View File

@@ -0,0 +1,347 @@
/*
* 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 constant pool item. Constant pool items can be created with the 'newXXX'
* methods in the {@link ClassWriter} class.
*
* @author Eric Bruneton
*/
final class Item {
/**
* Index of this item in the constant pool.
*/
int index;
/**
* Type of this constant pool item. A single class is used to represent all
* constant pool item types, in order to minimize the bytecode size of this
* package. The value of this field is one of {@link ClassWriter#INT},
* {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT},
* {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8},
* {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
* {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
* {@link ClassWriter#METH}, {@link ClassWriter#IMETH},
* {@link ClassWriter#MODULE}, {@link ClassWriter#PACKAGE},
* {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}.
*
* MethodHandle constant 9 variations are stored using a range of 9 values
* from {@link ClassWriter#HANDLE_BASE} + 1 to
* {@link ClassWriter#HANDLE_BASE} + 9.
*
* Special Item types are used for Items that are stored in the ClassWriter
* {@link ClassWriter#typeTable}, instead of the constant pool, in order to
* avoid clashes with normal constant pool items in the ClassWriter constant
* pool's hash table. These special item types are
* {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and
* {@link ClassWriter#TYPE_MERGED}.
*/
int type;
/**
* Value of this item, for an integer item.
*/
int intVal;
/**
* Value of this item, for a long item.
*/
long longVal;
/**
* First part of the value of this item, for items that do not hold a
* primitive value.
*/
String strVal1;
/**
* Second part of the value of this item, for items that do not hold a
* primitive value.
*/
String strVal2;
/**
* Third part of the value of this item, for items that do not hold a
* primitive value.
*/
String strVal3;
/**
* The hash code value of this constant pool item.
*/
int hashCode;
/**
* Link to another constant pool item, used for collision lists in the
* constant pool's hash table.
*/
Item next;
/**
* Constructs an uninitialized {@link Item}.
*/
Item() {
}
/**
* Constructs an uninitialized {@link Item} for constant pool element at
* given position.
*
* @param index
* index of the item to be constructed.
*/
Item(final int index) {
this.index = index;
}
/**
* Constructs a copy of the given item.
*
* @param index
* index of the item to be constructed.
* @param i
* the item that must be copied into the item to be constructed.
*/
Item(final int index, final Item i) {
this.index = index;
type = i.type;
intVal = i.intVal;
longVal = i.longVal;
strVal1 = i.strVal1;
strVal2 = i.strVal2;
strVal3 = i.strVal3;
hashCode = i.hashCode;
}
/**
* Sets this item to an integer item.
*
* @param intVal
* the value of this item.
*/
void set(final int intVal) {
this.type = ClassWriter.INT;
this.intVal = intVal;
this.hashCode = 0x7FFFFFFF & (type + intVal);
}
/**
* Sets this item to a long item.
*
* @param longVal
* the value of this item.
*/
void set(final long longVal) {
this.type = ClassWriter.LONG;
this.longVal = longVal;
this.hashCode = 0x7FFFFFFF & (type + (int) longVal);
}
/**
* Sets this item to a float item.
*
* @param floatVal
* the value of this item.
*/
void set(final float floatVal) {
this.type = ClassWriter.FLOAT;
this.intVal = Float.floatToRawIntBits(floatVal);
this.hashCode = 0x7FFFFFFF & (type + (int) floatVal);
}
/**
* Sets this item to a double item.
*
* @param doubleVal
* the value of this item.
*/
void set(final double doubleVal) {
this.type = ClassWriter.DOUBLE;
this.longVal = Double.doubleToRawLongBits(doubleVal);
this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal);
}
/**
* Sets this item to an item that do not hold a primitive value.
*
* @param type
* the type of this item.
* @param strVal1
* first part of the value of this item.
* @param strVal2
* second part of the value of this item.
* @param strVal3
* third part of the value of this item.
*/
@SuppressWarnings("fallthrough")
void set(final int type, final String strVal1, final String strVal2,
final String strVal3) {
this.type = type;
this.strVal1 = strVal1;
this.strVal2 = strVal2;
this.strVal3 = strVal3;
switch (type) {
case ClassWriter.CLASS:
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
case ClassWriter.UTF8:
case ClassWriter.STR:
case ClassWriter.MTYPE:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
case ClassWriter.TYPE_NORMAL:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
return;
case ClassWriter.NAME_TYPE: {
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
* strVal2.hashCode());
return;
}
// ClassWriter.FIELD:
// ClassWriter.METH:
// ClassWriter.IMETH:
// ClassWriter.HANDLE_BASE + 1..9
default:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
* strVal2.hashCode() * strVal3.hashCode());
}
}
/**
* Sets the item to an InvokeDynamic item.
*
* @param name
* invokedynamic's name.
* @param desc
* invokedynamic's desc.
* @param bsmIndex
* zero based index into the class attribute BootrapMethods.
*/
void set(String name, String desc, int bsmIndex) {
this.type = ClassWriter.INDY;
this.longVal = bsmIndex;
this.strVal1 = name;
this.strVal2 = desc;
this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex
* strVal1.hashCode() * strVal2.hashCode());
}
/**
* Sets the item to a BootstrapMethod item.
*
* @param position
* position in byte in the class attribute BootrapMethods.
* @param hashCode
* hashcode of the item. This hashcode is processed from the
* hashcode of the bootstrap method and the hashcode of all
* bootstrap arguments.
*/
void set(int position, int hashCode) {
this.type = ClassWriter.BSM;
this.intVal = position;
this.hashCode = hashCode;
}
/**
* Indicates if the given item is equal to this one. <i>This method assumes
* that the two items have the same {@link #type}</i>.
*
* @param i
* the item to be compared to this one. Both items must have the
* same {@link #type}.
* @return <tt>true</tt> if the given item if equal to this one,
* <tt>false</tt> otherwise.
*/
boolean isEqualTo(final Item i) {
switch (type) {
case ClassWriter.UTF8:
case ClassWriter.STR:
case ClassWriter.CLASS:
case ClassWriter.MODULE:
case ClassWriter.PACKAGE:
case ClassWriter.MTYPE:
case ClassWriter.TYPE_NORMAL:
return i.strVal1.equals(strVal1);
case ClassWriter.TYPE_MERGED:
case ClassWriter.LONG:
case ClassWriter.DOUBLE:
return i.longVal == longVal;
case ClassWriter.INT:
case ClassWriter.FLOAT:
return i.intVal == intVal;
case ClassWriter.TYPE_UNINIT:
return i.intVal == intVal && i.strVal1.equals(strVal1);
case ClassWriter.NAME_TYPE:
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2);
case ClassWriter.INDY: {
return i.longVal == longVal && i.strVal1.equals(strVal1)
&& i.strVal2.equals(strVal2);
}
// case ClassWriter.FIELD:
// case ClassWriter.METH:
// case ClassWriter.IMETH:
// case ClassWriter.HANDLE_BASE + 1..9
default:
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2)
&& i.strVal3.equals(strVal3);
}
}
}

View File

@@ -0,0 +1,593 @@
/*
* 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 label represents a position in the bytecode of a method. Labels are used
* for jump, goto, and switch instructions, and for try catch blocks. A label
* designates the <i>instruction</i> that is just after. Note however that there
* can be other elements between a label and the instruction it designates (such
* as other labels, stack map frames, line numbers, etc.).
*
* @author Eric Bruneton
*/
public class Label {
/**
* Indicates if this label is only used for debug attributes. Such a label
* is not the start of a basic block, the target of a jump instruction, or
* an exception handler. It can be safely ignored in control flow graph
* analysis algorithms (for optimization purposes).
*/
static final int DEBUG = 1;
/**
* Indicates if the position of this label is known.
*/
static final int RESOLVED = 2;
/**
* Indicates if this label has been updated, after instruction resizing.
*/
static final int RESIZED = 4;
/**
* Indicates if this basic block has been pushed in the basic block stack.
* See {@link MethodWriter#visitMaxs visitMaxs}.
*/
static final int PUSHED = 8;
/**
* Indicates if this label is the target of a jump instruction, or the start
* of an exception handler.
*/
static final int TARGET = 16;
/**
* Indicates if a stack map frame must be stored for this label.
*/
static final int STORE = 32;
/**
* Indicates if this label corresponds to a reachable basic block.
*/
static final int REACHABLE = 64;
/**
* Indicates if this basic block ends with a JSR instruction.
*/
static final int JSR = 128;
/**
* Indicates if this basic block ends with a RET instruction.
*/
static final int RET = 256;
/**
* Indicates if this basic block is the start of a subroutine.
*/
static final int SUBROUTINE = 512;
/**
* Indicates if this subroutine basic block has been visited by a
* visitSubroutine(null, ...) call.
*/
static final int VISITED = 1024;
/**
* Indicates if this subroutine basic block has been visited by a
* visitSubroutine(!null, ...) call.
*/
static final int VISITED2 = 2048;
/**
* Field used to associate user information to a label. Warning: this field
* is used by the ASM tree package. In order to use it with the ASM tree
* package you must override the
*
*/
public Object info;
/**
* Flags that indicate the status of this label.
*
* @see #DEBUG
* @see #RESOLVED
* @see #RESIZED
* @see #PUSHED
* @see #TARGET
* @see #STORE
* @see #REACHABLE
* @see #JSR
* @see #RET
*/
int status;
/**
* The line number corresponding to this label, if known. If there are
* several lines, each line is stored in a separate label, all linked via
* their next field (these links are created in ClassReader and removed just
* before visitLabel is called, so that this does not impact the rest of the
* code).
*/
int line;
/**
* The position of this label in the code, if known.
*/
int position;
/**
* Number of forward references to this label, times two.
*/
private int referenceCount;
/**
* Informations about forward references. Each forward reference is
* described by two consecutive integers in this array: the first one is the
* position of the first byte of the bytecode instruction that contains the
* forward reference, while the second is the position of the first byte of
* the forward reference itself. In fact the sign of the first integer
* indicates if this reference uses 2 or 4 bytes, and its absolute value
* gives the position of the bytecode instruction. This array is also used
* as a bitset to store the subroutines to which a basic block belongs. This
* information is needed in {@linked MethodWriter#visitMaxs}, after all
* forward references have been resolved. Hence the same array can be used
* for both purposes without problems.
*/
private int[] srcAndRefPositions;
// ------------------------------------------------------------------------
/*
* Fields for the control flow and data flow graph analysis algorithms (used
* to compute the maximum stack size or the stack map frames). A control
* flow graph contains one node per "basic block", and one edge per "jump"
* from one basic block to another. Each node (i.e., each basic block) is
* represented by the Label object that corresponds to the first instruction
* of this basic block. Each node also stores the list of its successors in
* the graph, as a linked list of Edge objects.
*
* The control flow analysis algorithms used to compute the maximum stack
* size or the stack map frames are similar and use two steps. The first
* step, during the visit of each instruction, builds information about the
* state of the local variables and the operand stack at the end of each
* basic block, called the "output frame", <i>relatively</i> to the frame
* state at the beginning of the basic block, which is called the "input
* frame", and which is <i>unknown</i> during this step. The second step, in
* {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes
* information about the input frame of each basic block, from the input
* state of the first basic block (known from the method signature), and by
* the using the previously computed relative output frames.
*
* The algorithm used to compute the maximum stack size only computes the
* relative output and absolute input stack heights, while the algorithm
* used to compute stack map frames computes relative output frames and
* absolute input frames.
*/
/**
* Start of the output stack relatively to the input stack. The exact
* semantics of this field depends on the algorithm that is used.
*
* When only the maximum stack size is computed, this field is the number of
* elements in the input stack.
*
* When the stack map frames are completely computed, this field is the
* offset of the first output stack element relatively to the top of the
* input stack. This offset is always negative or null. A null offset means
* that the output stack must be appended to the input stack. A -n offset
* means that the first n output stack elements must replace the top n input
* stack elements, and that the other elements must be appended to the input
* stack.
*/
int inputStackTop;
/**
* Maximum height reached by the output stack, relatively to the top of the
* input stack. This maximum is always positive or null.
*/
int outputStackMax;
/**
* Information about the input and output stack map frames of this basic
* block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
* option is used.
*/
Frame frame;
/**
* The successor of this label, in the order they are visited. This linked
* list does not include labels used for debug info only. If
* {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
* does not contain successive labels that denote the same bytecode position
* (in this case only the first label appears in this list).
*/
Label successor;
/**
* The successors of this node in the control flow graph. These successors
* are stored in a linked list of {@link Edge Edge} objects, linked to each
* other by their {@link Edge#next} field.
*/
Edge successors;
/**
* The next basic block in the basic block stack. This stack is used in the
* main loop of the fix point algorithm used in the second step of the
* control flow analysis algorithms. It is also used in
* {@link #visitSubroutine} to avoid using a recursive method, and in
* ClassReader to temporarily store multiple source lines for a label.
*
* @see MethodWriter#visitMaxs
*/
Label next;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new label.
*/
public Label() {
}
// ------------------------------------------------------------------------
// Methods to compute offsets and to manage forward references
// ------------------------------------------------------------------------
/**
* Returns the offset corresponding to this label. This offset is computed
* from the start of the method's bytecode. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @return the offset corresponding to this label.
* @throws IllegalStateException
* if this label is not resolved yet.
*/
public int getOffset() {
if ((status & RESOLVED) == 0) {
throw new IllegalStateException(
"Label offset position has not been resolved yet");
}
return position;
}
/**
* Puts a reference to this label in the bytecode of a method. If the
* position of the label is known, the offset is computed and written
* directly. Otherwise, a null offset is written and a new forward reference
* is declared for this label.
*
* @param owner
* the code writer that calls this method.
* @param out
* the bytecode of the method.
* @param source
* the position of first byte of the bytecode instruction that
* contains this label.
* @param wideOffset
* <tt>true</tt> if the reference must be stored in 4 bytes, or
* <tt>false</tt> if it must be stored with 2 bytes.
* @throws IllegalArgumentException
* if this label has not been created by the given code writer.
*/
void put(final MethodWriter owner, final ByteVector out, final int source,
final boolean wideOffset) {
if ((status & RESOLVED) == 0) {
if (wideOffset) {
addReference(-1 - source, out.length);
out.putInt(-1);
} else {
addReference(source, out.length);
out.putShort(-1);
}
} else {
if (wideOffset) {
out.putInt(position - source);
} else {
out.putShort(position - source);
}
}
}
/**
* Adds a forward reference to this label. This method must be called only
* for a true forward reference, i.e. only if this label is not resolved
* yet. For backward references, the offset of the reference can be, and
* must be, computed and stored directly.
*
* @param sourcePosition
* the position of the referencing instruction. This position
* will be used to compute the offset of this forward reference.
* @param referencePosition
* the position where the offset for this forward reference must
* be stored.
*/
private void addReference(final int sourcePosition,
final int referencePosition) {
if (srcAndRefPositions == null) {
srcAndRefPositions = new int[6];
}
if (referenceCount >= srcAndRefPositions.length) {
int[] a = new int[srcAndRefPositions.length + 6];
System.arraycopy(srcAndRefPositions, 0, a, 0,
srcAndRefPositions.length);
srcAndRefPositions = a;
}
srcAndRefPositions[referenceCount++] = sourcePosition;
srcAndRefPositions[referenceCount++] = referencePosition;
}
/**
* Resolves all forward references to this label. This method must be called
* when this label is added to the bytecode of the method, i.e. when its
* position becomes known. This method fills in the blanks that where left
* in the bytecode by each forward reference previously added to this label.
*
* @param owner
* the code writer that calls this method.
* @param position
* the position of this label in the bytecode.
* @param data
* the bytecode of the method.
* @return <tt>true</tt> if a blank that was left for this label was too
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo
* instructions will be replaced with standard bytecode instructions
* with wider offsets (4 bytes instead of 2), in ClassReader.
* @throws IllegalArgumentException
* if this label has already been resolved, or if it has not
* been created by the given code writer.
*/
boolean resolve(final MethodWriter owner, final int position,
final byte[] data) {
boolean needUpdate = false;
this.status |= RESOLVED;
this.position = position;
int i = 0;
while (i < referenceCount) {
int source = srcAndRefPositions[i++];
int reference = srcAndRefPositions[i++];
int offset;
if (source >= 0) {
offset = position - source;
if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) {
/*
* changes the opcode of the jump instruction, in order to
* be able to find it later (see resizeInstructions in
* MethodWriter). These temporary opcodes are similar to
* jump instruction opcodes, except that the 2 bytes offset
* is unsigned (and can therefore represent values from 0 to
* 65535, which is sufficient since the size of a method is
* limited to 65535 bytes).
*/
int opcode = data[reference - 1] & 0xFF;
if (opcode <= Opcodes.JSR) {
// changes IFEQ ... JSR to opcodes 202 to 217
data[reference - 1] = (byte) (opcode + 49);
} else {
// changes IFNULL and IFNONNULL to opcodes 218 and 219
data[reference - 1] = (byte) (opcode + 20);
}
needUpdate = true;
}
data[reference++] = (byte) (offset >>> 8);
data[reference] = (byte) offset;
} else {
offset = position + source + 1;
data[reference++] = (byte) (offset >>> 24);
data[reference++] = (byte) (offset >>> 16);
data[reference++] = (byte) (offset >>> 8);
data[reference] = (byte) offset;
}
}
return needUpdate;
}
/**
* Returns the first label of the series to which this label belongs. For an
* isolated label or for the first label in a series of successive labels,
* this method returns the label itself. For other labels it returns the
* first label of the series.
*
* @return the first label of the series to which this label belongs.
*/
Label getFirst() {
return frame == null ? this : frame.owner;
}
// ------------------------------------------------------------------------
// Methods related to subroutines
// ------------------------------------------------------------------------
/**
* Returns true is this basic block belongs to the given subroutine.
*
* @param id
* a subroutine id.
* @return true is this basic block belongs to the given subroutine.
*/
boolean inSubroutine(final long id) {
if ((status & Label.VISITED) != 0) {
return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0;
}
return false;
}
/**
* Returns true if this basic block and the given one belong to a common
* subroutine.
*
* @param block
* another basic block.
* @return true if this basic block and the given one belong to a common
* subroutine.
*/
boolean inSameSubroutine(final Label block) {
if ((status & VISITED) == 0 || (block.status & VISITED) == 0) {
return false;
}
for (int i = 0; i < srcAndRefPositions.length; ++i) {
if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) {
return true;
}
}
return false;
}
/**
* Marks this basic block as belonging to the given subroutine.
*
* @param id
* a subroutine id.
* @param nbSubroutines
* the total number of subroutines in the method.
*/
void addToSubroutine(final long id, final int nbSubroutines) {
if ((status & VISITED) == 0) {
status |= VISITED;
srcAndRefPositions = new int[nbSubroutines / 32 + 1];
}
srcAndRefPositions[(int) (id >>> 32)] |= (int) id;
}
/**
* Finds the basic blocks that belong to a given subroutine, and marks these
* blocks as belonging to this subroutine. This method follows the control
* flow graph to find all the blocks that are reachable from the current
* block WITHOUT following any JSR target.
*
* @param JSR
* a JSR block that jumps to this subroutine. If this JSR is not
* null it is added to the successor of the RET blocks found in
* the subroutine.
* @param id
* the id of this subroutine.
* @param nbSubroutines
* the total number of subroutines in the method.
*/
void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) {
// user managed stack of labels, to avoid using a recursive method
// (recursivity can lead to stack overflow with very large methods)
Label stack = this;
while (stack != null) {
// removes a label l from the stack
Label l = stack;
stack = l.next;
l.next = null;
if (JSR != null) {
if ((l.status & VISITED2) != 0) {
continue;
}
l.status |= VISITED2;
// adds JSR to the successors of l, if it is a RET block
if ((l.status & RET) != 0) {
if (!l.inSameSubroutine(JSR)) {
Edge e = new Edge();
e.info = l.inputStackTop;
e.successor = JSR.successors.successor;
e.next = l.successors;
l.successors = e;
}
}
} else {
// if the l block already belongs to subroutine 'id', continue
if (l.inSubroutine(id)) {
continue;
}
// marks the l block as belonging to subroutine 'id'
l.addToSubroutine(id, nbSubroutines);
}
// pushes each successor of l on the stack, except JSR targets
Edge e = l.successors;
while (e != null) {
// if the l block is a JSR block, then 'l.successors.next' leads
// to the JSR target (see {@link #visitJumpInsn}) and must
// therefore not be followed
if ((l.status & Label.JSR) == 0 || e != l.successors.next) {
// pushes e.successor on the stack if it not already added
if (e.successor.next == null) {
e.successor.next = stack;
stack = e.successor;
}
}
e = e.next;
}
}
}
// ------------------------------------------------------------------------
// Overriden Object methods
// ------------------------------------------------------------------------
/**
* Returns a string representation of this label.
*
* @return a string representation of this label.
*/
@Override
public String toString() {
return "L" + System.identityHashCode(this);
}
}

View File

@@ -3,10 +3,9 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.util;
package org.redkale.asm;
import java.util.*;
import jdk.internal.org.objectweb.asm.*;
/**
* MethodVisitor 的调试类
@@ -15,13 +14,13 @@ import jdk.internal.org.objectweb.asm.*;
*
* @author zhangjx
*/
public class AsmMethodVisitor {
public class MethodDebugVisitor {
private final MethodVisitor visitor;
private boolean debug = false;
public AsmMethodVisitor setDebug(boolean d) {
public MethodDebugVisitor setDebug(boolean d) {
debug = d;
return this;
}
@@ -33,7 +32,7 @@ public class AsmMethodVisitor {
System.out.println();
}
private final Map<Label, Integer> labels = new LinkedHashMap();
private final Map<Label, Integer> labels = new LinkedHashMap<>();
private static final String[] opcodes = new String[200]; //0 -18
@@ -55,7 +54,7 @@ public class AsmMethodVisitor {
}
}
public AsmMethodVisitor(MethodVisitor visitor) {
public MethodDebugVisitor(MethodVisitor visitor) {
//super(Opcodes.ASM5, visitor);
this.visitor = visitor;
}
@@ -173,7 +172,7 @@ public class AsmMethodVisitor {
if (debug) {
if (o instanceof CharSequence) {
System.out.println("mv.visitLdcInsn(\"" + o + "\");");
} else if (o instanceof jdk.internal.org.objectweb.asm.Type) {
} else if (o instanceof org.redkale.asm.Type) {
System.out.println("mv.visitLdcInsn(Type.getType(\"" + o + "\"));");
} else {
System.out.println("mv.visitLdcInsn(" + o + ");");

View File

@@ -0,0 +1,856 @@
/*
* 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 method. The methods of this class must be called in
* the following order: ( <tt>visitParameter</tt> )* [
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in
* the sequential order of the bytecode instructions of the visited code,
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
* labels passed as arguments have been visited,
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the
* corresponding try catch block has been visited, and the
* <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and
* <tt>visitLineNumber</tt> methods must be called <i>after</i> the labels
* passed as arguments have been visited.
*
* @author Eric Bruneton
*/
public abstract class MethodVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
protected final int api;
/**
* The method visitor to which this visitor must delegate method calls. May
* be null.
*/
protected MethodVisitor mv;
/**
* Constructs a new {@link MethodVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
*/
public MethodVisitor(final int api) {
this(api, null);
}
/**
* Constructs a new {@link MethodVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
* @param mv
* the method visitor to which this visitor must delegate method
* calls. May be null.
*/
public MethodVisitor(final int api, final MethodVisitor mv) {
this.api = api;
this.mv = mv;
}
// -------------------------------------------------------------------------
// Parameters, annotations and non standard attributes
// -------------------------------------------------------------------------
/**
* Visits a parameter of this method.
*
* @param name
* parameter name or null if none is provided.
* @param access
* the parameter's access flags, only <tt>ACC_FINAL</tt>,
* <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> are
* allowed (see {@link Opcodes}).
*/
public void visitParameter(String name, int access) {
if (mv != null) {
mv.visitParameter(name, access);
}
}
/**
* Visits the default value of this annotation interface method.
*
* @return a visitor to the visit the actual default value of this
* annotation interface method, or <tt>null</tt> if this visitor is
* not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this
* annotation visitor, followed by visitEnd.
*/
public AnnotationVisitor visitAnnotationDefault() {
if (mv != null) {
return mv.visitAnnotationDefault();
}
return null;
}
/**
* Visits an annotation of this method.
*
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (mv != null) {
return mv.visitAnnotation(desc, visible);
}
return null;
}
/**
* Visits an annotation on a type in the method signature.
*
* @param typeRef
* a reference to the annotated type. The sort of this type
* reference must be {@link TypeReference#METHOD_TYPE_PARAMETER
* METHOD_TYPE_PARAMETER},
* {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND
* METHOD_TYPE_PARAMETER_BOUND},
* {@link TypeReference#METHOD_RETURN METHOD_RETURN},
* {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER},
* {@link TypeReference#METHOD_FORMAL_PARAMETER
* METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS
* THROWS}. See {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (mv != null) {
return mv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
return null;
}
/**
* Visits an annotation of a parameter this method.
*
* @param parameter
* the parameter index.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitParameterAnnotation(int parameter,
String desc, boolean visible) {
if (mv != null) {
return mv.visitParameterAnnotation(parameter, desc, visible);
}
return null;
}
/**
* Visits a non standard attribute of this method.
*
* @param attr
* an attribute.
*/
public void visitAttribute(Attribute attr) {
if (mv != null) {
mv.visitAttribute(attr);
}
}
/**
* Starts the visit of the method's code, if any (i.e. non abstract method).
*/
public void visitCode() {
if (mv != null) {
mv.visitCode();
}
}
/**
* Visits the current state of the local variables and operand stack
* elements. This method must(*) be called <i>just before</i> any
* instruction <b>i</b> that follows an unconditional branch instruction
* such as GOTO or THROW, that is the target of a jump instruction, or that
* starts an exception handler block. The visited types must describe the
* values of the local variables and of the operand stack elements <i>just
* before</i> <b>i</b> is executed.<br>
* <br>
* (*) this is mandatory only for classes whose version is greater than or
* equal to {@link Opcodes#V1_6 V1_6}. <br>
* <br>
* The frames of a method must be given either in expanded form, or in
* compressed form (all frames must use the same format, i.e. you must not
* mix expanded and compressed frames within a single method):
* <ul>
* <li>In expanded form, all frames must have the F_NEW type.</li>
* <li>In compressed form, frames are basically "deltas" from the state of
* the previous frame:
* <ul>
* <li>{@link Opcodes#F_SAME} representing frame with exactly the same
* locals as the previous frame and with the empty stack.</li>
* <li>{@link Opcodes#F_SAME1} representing frame with exactly the same
* locals as the previous frame and with single value on the stack (
* <code>nStack</code> is 1 and <code>stack[0]</code> contains value for the
* type of the stack item).</li>
* <li>{@link Opcodes#F_APPEND} representing frame with current locals are
* the same as the locals in the previous frame, except that additional
* locals are defined (<code>nLocal</code> is 1, 2 or 3 and
* <code>local</code> elements contains values representing added types).</li>
* <li>{@link Opcodes#F_CHOP} representing frame with current locals are the
* same as the locals in the previous frame, except that the last 1-3 locals
* are absent and with the empty stack (<code>nLocals</code> is 1, 2 or 3).</li>
* <li>{@link Opcodes#F_FULL} representing complete frame data.</li>
* </ul>
* </li>
* </ul>
* <br>
* In both cases the first frame, corresponding to the method's parameters
* and access flags, is implicit and must not be visited. Also, it is
* illegal to visit two or more frames for the same code location (i.e., at
* least one instruction must be visited between two calls to visitFrame).
*
* @param type
* the type of this stack map frame. Must be
* {@link Opcodes#F_NEW} for expanded frames, or
* {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND},
* {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or
* {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for
* compressed frames.
* @param nLocal
* the number of local variables in the visited frame.
* @param local
* the local variable types in this frame. This array must not be
* modified. 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 in the visited frame.
* @param stack
* the operand stack types in this frame. This array must not be
* modified. Its content has the same format as the "local"
* array.
* @throws IllegalStateException
* if a frame is visited just after another one, without any
* instruction between the two (unless this frame is a
* Opcodes#F_SAME frame, in which case it is silently ignored).
*/
public void visitFrame(int type, int nLocal, Object[] local, int nStack,
Object[] stack) {
if (mv != null) {
mv.visitFrame(type, nLocal, local, nStack, stack);
}
}
// -------------------------------------------------------------------------
// Normal instructions
// -------------------------------------------------------------------------
/**
* Visits a zero operand instruction.
*
* @param opcode
* the opcode of the instruction to be visited. This opcode is
* either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1,
* ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1,
* FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD,
* LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD,
* IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE,
* SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1,
* DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB,
* IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM,
* FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR,
* IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D,
* L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S,
* LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN,
* DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER,
* or MONITOREXIT.
*/
public void visitInsn(int opcode) {
if (mv != null) {
mv.visitInsn(opcode);
}
}
/**
* Visits an instruction with a single int operand.
*
* @param opcode
* the opcode of the instruction to be visited. This opcode is
* either BIPUSH, SIPUSH or NEWARRAY.
* @param operand
* the operand of the instruction to be visited.<br>
* When opcode is BIPUSH, operand value should be between
* Byte.MIN_VALUE and Byte.MAX_VALUE.<br>
* When opcode is SIPUSH, operand value should be between
* Short.MIN_VALUE and Short.MAX_VALUE.<br>
* When opcode is NEWARRAY, operand value should be one of
* {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR},
* {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE},
* {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT},
* {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}.
*/
public void visitIntInsn(int opcode, int operand) {
if (mv != null) {
mv.visitIntInsn(opcode, operand);
}
}
/**
* Visits a local variable instruction. A local variable instruction is an
* instruction that loads or stores the value of a local variable.
*
* @param opcode
* the opcode of the local variable instruction to be visited.
* This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD,
* ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
* @param var
* the operand of the instruction to be visited. This operand is
* the index of a local variable.
*/
public void visitVarInsn(int opcode, int var) {
if (mv != null) {
mv.visitVarInsn(opcode, var);
}
}
/**
* Visits a type instruction. A type instruction is an instruction that
* takes the internal name of a class as parameter.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
* @param type
* the operand of the instruction to be visited. This operand
* must be the internal name of an object or array class (see
* {@link Type#getInternalName() getInternalName}).
*/
public void visitTypeInsn(int opcode, String type) {
if (mv != null) {
mv.visitTypeInsn(opcode, type);
}
}
/**
* Visits a field instruction. A field instruction is an instruction that
* loads or stores the value of a field of an object.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
* @param owner
* the internal name of the field's owner class (see
* {@link Type#getInternalName() getInternalName}).
* @param name
* the field's name.
* @param desc
* the field's descriptor (see {@link Type Type}).
*/
public void visitFieldInsn(int opcode, String owner, String name,
String desc) {
if (mv != null) {
mv.visitFieldInsn(opcode, owner, name, desc);
}
}
/**
* 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}).
* @param itf
* if the method's owner class is an interface.
*/
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
/**
* Visits an invokedynamic instruction.
*
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param bsm
* the bootstrap method.
* @param bsmArgs
* the bootstrap method constant arguments. Each argument must be
* an {@link Integer}, {@link Float}, {@link Long},
* {@link Double}, {@link String}, {@link Type} or {@link Handle}
* value. This method is allowed to modify the content of the
* array so a caller should expect that this array may change.
*/
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
if (mv != null) {
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
}
/**
* Visits a jump instruction. A jump instruction is an instruction that may
* jump to another instruction.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ,
* IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
* IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
* @param label
* the operand of the instruction to be visited. This operand is
* a label that designates the instruction to which the jump
* instruction may jump.
*/
public void visitJumpInsn(int opcode, Label label) {
if (mv != null) {
mv.visitJumpInsn(opcode, label);
}
}
/**
* Visits a label. A label designates the instruction that will be visited
* just after it.
*
* @param label
* a {@link Label Label} object.
*/
public void visitLabel(Label label) {
if (mv != null) {
mv.visitLabel(label);
}
}
// -------------------------------------------------------------------------
// Special instructions
// -------------------------------------------------------------------------
/**
* Visits a LDC instruction. Note that new constant types may be added in
* future versions of the Java Virtual Machine. To easily detect new
* constant types, implementations of this method should check for
* unexpected constant types, like this:
*
* <pre>
* if (cst instanceof Integer) {
* // ...
* } else if (cst instanceof Float) {
* // ...
* } else if (cst instanceof Long) {
* // ...
* } else if (cst instanceof Double) {
* // ...
* } else if (cst instanceof String) {
* // ...
* } else if (cst instanceof Type) {
* int sort = ((Type) cst).getSort();
* if (sort == Type.OBJECT) {
* // ...
* } else if (sort == Type.ARRAY) {
* // ...
* } else if (sort == Type.METHOD) {
* // ...
* } else {
* // throw an exception
* }
* } else if (cst instanceof Handle) {
* // ...
* } else {
* // throw an exception
* }
* </pre>
*
* @param cst
* the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for <tt>.class</tt> constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0.
*/
public void visitLdcInsn(Object cst) {
if (mv != null) {
mv.visitLdcInsn(cst);
}
}
/**
* Visits an IINC instruction.
*
* @param var
* index of the local variable to be incremented.
* @param increment
* amount to increment the local variable by.
*/
public void visitIincInsn(int var, int increment) {
if (mv != null) {
mv.visitIincInsn(var, increment);
}
}
/**
* Visits a TABLESWITCH instruction.
*
* @param min
* the minimum key value.
* @param max
* the maximum key value.
* @param dflt
* beginning of the default handler block.
* @param labels
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
* beginning of the handler block for the <tt>min + i</tt> key.
*/
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) {
if (mv != null) {
mv.visitTableSwitchInsn(min, max, dflt, labels);
}
}
/**
* Visits a LOOKUPSWITCH instruction.
*
* @param dflt
* beginning of the default handler block.
* @param keys
* the values of the keys.
* @param labels
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
* beginning of the handler block for the <tt>keys[i]</tt> key.
*/
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) {
mv.visitLookupSwitchInsn(dflt, keys, labels);
}
}
/**
* Visits a MULTIANEWARRAY instruction.
*
* @param desc
* an array type descriptor (see {@link Type Type}).
* @param dims
* number of dimensions of the array to allocate.
*/
public void visitMultiANewArrayInsn(String desc, int dims) {
if (mv != null) {
mv.visitMultiANewArrayInsn(desc, dims);
}
}
/**
* Visits an annotation on an instruction. This method must be called just
* <i>after</i> the annotated instruction. It can be called several times
* for the same instruction.
*
* @param typeRef
* a reference to the annotated type. The sort of this type
* reference must be {@link TypeReference#INSTANCEOF INSTANCEOF},
* {@link TypeReference#NEW NEW},
* {@link TypeReference#CONSTRUCTOR_REFERENCE
* CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE
* METHOD_REFERENCE}, {@link TypeReference#CAST CAST},
* {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
* CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
* {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT
* METHOD_INVOCATION_TYPE_ARGUMENT},
* {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
* CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
* {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT
* METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (mv != null) {
return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
}
return null;
}
// -------------------------------------------------------------------------
// Exceptions table entries, debug information, max stack and max locals
// -------------------------------------------------------------------------
/**
* Visits a try catch block.
*
* @param start
* beginning of the exception handler's scope (inclusive).
* @param end
* end of the exception handler's scope (exclusive).
* @param handler
* beginning of the exception handler's code.
* @param type
* internal name of the type of exceptions handled by the
* handler, or <tt>null</tt> to catch any exceptions (for
* "finally" blocks).
* @throws IllegalArgumentException
* if one of the labels has already been visited by this visitor
* (by the {@link #visitLabel visitLabel} method).
*/
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type) {
if (mv != null) {
mv.visitTryCatchBlock(start, end, handler, type);
}
}
/**
* Visits an annotation on an exception handler type. This method must be
* called <i>after</i> the {@link #visitTryCatchBlock} for the annotated
* exception handler. It can be called several times for the same exception
* handler.
*
* @param typeRef
* a reference to the annotated type. The sort of this type
* reference must be {@link TypeReference#EXCEPTION_PARAMETER
* EXCEPTION_PARAMETER}. See {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (mv != null) {
return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
}
return null;
}
/**
* Visits a local variable declaration.
*
* @param name
* the name of a local variable.
* @param desc
* the type descriptor of this local variable.
* @param signature
* the type signature of this local variable. May be
* <tt>null</tt> if the local variable type does not use generic
* types.
* @param start
* the first instruction corresponding to the scope of this local
* variable (inclusive).
* @param end
* the last instruction corresponding to the scope of this local
* variable (exclusive).
* @param index
* the local variable's index.
* @throws IllegalArgumentException
* if one of the labels has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method).
*/
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
if (mv != null) {
mv.visitLocalVariable(name, desc, signature, start, end, index);
}
}
/**
* Visits an annotation on a local variable type.
*
* @param typeRef
* a reference to the annotated type. The sort of this type
* reference must be {@link TypeReference#LOCAL_VARIABLE
* LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE
* RESOURCE_VARIABLE}. See {@link TypeReference}.
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
* @param start
* the fist instructions corresponding to the continuous ranges
* that make the scope of this local variable (inclusive).
* @param end
* the last instructions corresponding to the continuous ranges
* that make the scope of this local variable (exclusive). This
* array must have the same size as the 'start' array.
* @param index
* the local variable's index in each range. This array must have
* the same size as the 'start' array.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <tt>true</tt> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) {
if (mv != null) {
return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
end, index, desc, visible);
}
return null;
}
/**
* Visits a line number declaration.
*
* @param line
* a line number. This number refers to the source file from
* which the class was compiled.
* @param start
* the first instruction corresponding to this line number.
* @throws IllegalArgumentException
* if <tt>start</tt> has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method).
*/
public void visitLineNumber(int line, Label start) {
if (mv != null) {
mv.visitLineNumber(line, start);
}
}
/**
* Visits the maximum stack size and the maximum number of local variables
* of the method.
*
* @param maxStack
* maximum stack size of the method.
* @param maxLocals
* maximum number of local variables for the method.
*/
public void visitMaxs(int maxStack, int maxLocals) {
if (mv != null) {
mv.visitMaxs(maxStack, maxLocals);
}
}
/**
* Visits the end of the method. This method, which is the last one to be
* called, is used to inform the visitor that all the annotations and
* attributes of the method have been visited.
*/
public void visitEnd() {
if (mv != null) {
mv.visitEnd();
}
}
}

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

@@ -0,0 +1,398 @@
/*
* 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;
/**
* Defines the JVM opcodes, access flags and array type codes. This interface
* does not define all the JVM opcodes because some opcodes are automatically
* handled. For example, the xLOAD and xSTORE opcodes are automatically replaced
* by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n
* opcodes are therefore not defined in this interface. Likewise for LDC,
* automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and
* JSR_W.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public interface Opcodes {
// ASM API versions
int ASM4 = 4 << 16 | 0 << 8 | 0;
int ASM5 = 5 << 16 | 0 << 8 | 0;
int ASM6 = 6 << 16 | 0 << 8 | 0;
// versions
int V1_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51;
int V1_8 = 0 << 16 | 52;
int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54;
// access flags
int ACC_PUBLIC = 0x0001; // class, field, method
int ACC_PRIVATE = 0x0002; // class, field, method
int ACC_PROTECTED = 0x0004; // class, field, method
int ACC_STATIC = 0x0008; // field, method
int ACC_FINAL = 0x0010; // class, field, method, parameter
int ACC_SUPER = 0x0020; // class
int ACC_SYNCHRONIZED = 0x0020; // method
int ACC_OPEN = 0x0020; // module
int ACC_TRANSITIVE = 0x0020; // module requires
int ACC_VOLATILE = 0x0040; // field
int ACC_BRIDGE = 0x0040; // method
int ACC_STATIC_PHASE = 0x0040; // module requires
int ACC_VARARGS = 0x0080; // method
int ACC_TRANSIENT = 0x0080; // field
int ACC_NATIVE = 0x0100; // method
int ACC_INTERFACE = 0x0200; // class
int ACC_ABSTRACT = 0x0400; // class, method
int ACC_STRICT = 0x0800; // method
int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module *
int ACC_ANNOTATION = 0x2000; // class
int ACC_ENUM = 0x4000; // class(?) field inner
int ACC_MANDATED = 0x8000; // parameter, module, module *
int ACC_MODULE = 0x8000; // class
// ASM specific pseudo access flags
int ACC_DEPRECATED = 0x20000; // class, field, method
// types for NEWARRAY
int T_BOOLEAN = 4;
int T_CHAR = 5;
int T_FLOAT = 6;
int T_DOUBLE = 7;
int T_BYTE = 8;
int T_SHORT = 9;
int T_INT = 10;
int T_LONG = 11;
// tags for Handle
int H_GETFIELD = 1;
int H_GETSTATIC = 2;
int H_PUTFIELD = 3;
int H_PUTSTATIC = 4;
int H_INVOKEVIRTUAL = 5;
int H_INVOKESTATIC = 6;
int H_INVOKESPECIAL = 7;
int H_NEWINVOKESPECIAL = 8;
int H_INVOKEINTERFACE = 9;
// stack map frame types
/**
* Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}.
*/
int F_NEW = -1;
/**
* Represents a compressed frame with complete frame data.
*/
int F_FULL = 0;
/**
* Represents a compressed frame where locals are the same as the locals in
* the previous frame, except that additional 1-3 locals are defined, and
* with an empty stack.
*/
int F_APPEND = 1;
/**
* Represents a compressed frame where locals are the same as the locals in
* the previous frame, except that the last 1-3 locals are absent and with
* an empty stack.
*/
int F_CHOP = 2;
/**
* Represents a compressed frame with exactly the same locals as the
* previous frame and with an empty stack.
*/
int F_SAME = 3;
/**
* Represents a compressed frame with exactly the same locals as the
* previous frame and with a single value on the stack.
*/
int F_SAME1 = 4;
// Do not try to change the following code to use auto-boxing,
// these values are compared by reference and not by value
// The constructor of Integer was deprecated in 9
// but we are stuck with it by backward compatibility
@SuppressWarnings("deprecation") Integer TOP = new Integer(0);
@SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
@SuppressWarnings("deprecation") Integer FLOAT = new Integer(2);
@SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3);
@SuppressWarnings("deprecation") Integer LONG = new Integer(4);
@SuppressWarnings("deprecation") Integer NULL = new Integer(5);
@SuppressWarnings("deprecation") Integer UNINITIALIZED_THIS = new Integer(6);
// opcodes // visit method (- = idem)
int NOP = 0; // visitInsn
int ACONST_NULL = 1; // -
int ICONST_M1 = 2; // -
int ICONST_0 = 3; // -
int ICONST_1 = 4; // -
int ICONST_2 = 5; // -
int ICONST_3 = 6; // -
int ICONST_4 = 7; // -
int ICONST_5 = 8; // -
int LCONST_0 = 9; // -
int LCONST_1 = 10; // -
int FCONST_0 = 11; // -
int FCONST_1 = 12; // -
int FCONST_2 = 13; // -
int DCONST_0 = 14; // -
int DCONST_1 = 15; // -
int BIPUSH = 16; // visitIntInsn
int SIPUSH = 17; // -
int LDC = 18; // visitLdcInsn
// int LDC_W = 19; // -
// int LDC2_W = 20; // -
int ILOAD = 21; // visitVarInsn
int LLOAD = 22; // -
int FLOAD = 23; // -
int DLOAD = 24; // -
int ALOAD = 25; // -
// int ILOAD_0 = 26; // -
// int ILOAD_1 = 27; // -
// int ILOAD_2 = 28; // -
// int ILOAD_3 = 29; // -
// int LLOAD_0 = 30; // -
// int LLOAD_1 = 31; // -
// int LLOAD_2 = 32; // -
// int LLOAD_3 = 33; // -
// int FLOAD_0 = 34; // -
// int FLOAD_1 = 35; // -
// int FLOAD_2 = 36; // -
// int FLOAD_3 = 37; // -
// int DLOAD_0 = 38; // -
// int DLOAD_1 = 39; // -
// int DLOAD_2 = 40; // -
// int DLOAD_3 = 41; // -
// int ALOAD_0 = 42; // -
// int ALOAD_1 = 43; // -
// int ALOAD_2 = 44; // -
// int ALOAD_3 = 45; // -
int IALOAD = 46; // visitInsn
int LALOAD = 47; // -
int FALOAD = 48; // -
int DALOAD = 49; // -
int AALOAD = 50; // -
int BALOAD = 51; // -
int CALOAD = 52; // -
int SALOAD = 53; // -
int ISTORE = 54; // visitVarInsn
int LSTORE = 55; // -
int FSTORE = 56; // -
int DSTORE = 57; // -
int ASTORE = 58; // -
// int ISTORE_0 = 59; // -
// int ISTORE_1 = 60; // -
// int ISTORE_2 = 61; // -
// int ISTORE_3 = 62; // -
// int LSTORE_0 = 63; // -
// int LSTORE_1 = 64; // -
// int LSTORE_2 = 65; // -
// int LSTORE_3 = 66; // -
// int FSTORE_0 = 67; // -
// int FSTORE_1 = 68; // -
// int FSTORE_2 = 69; // -
// int FSTORE_3 = 70; // -
// int DSTORE_0 = 71; // -
// int DSTORE_1 = 72; // -
// int DSTORE_2 = 73; // -
// int DSTORE_3 = 74; // -
// int ASTORE_0 = 75; // -
// int ASTORE_1 = 76; // -
// int ASTORE_2 = 77; // -
// int ASTORE_3 = 78; // -
int IASTORE = 79; // visitInsn
int LASTORE = 80; // -
int FASTORE = 81; // -
int DASTORE = 82; // -
int AASTORE = 83; // -
int BASTORE = 84; // -
int CASTORE = 85; // -
int SASTORE = 86; // -
int POP = 87; // -
int POP2 = 88; // -
int DUP = 89; // -
int DUP_X1 = 90; // -
int DUP_X2 = 91; // -
int DUP2 = 92; // -
int DUP2_X1 = 93; // -
int DUP2_X2 = 94; // -
int SWAP = 95; // -
int IADD = 96; // -
int LADD = 97; // -
int FADD = 98; // -
int DADD = 99; // -
int ISUB = 100; // -
int LSUB = 101; // -
int FSUB = 102; // -
int DSUB = 103; // -
int IMUL = 104; // -
int LMUL = 105; // -
int FMUL = 106; // -
int DMUL = 107; // -
int IDIV = 108; // -
int LDIV = 109; // -
int FDIV = 110; // -
int DDIV = 111; // -
int IREM = 112; // -
int LREM = 113; // -
int FREM = 114; // -
int DREM = 115; // -
int INEG = 116; // -
int LNEG = 117; // -
int FNEG = 118; // -
int DNEG = 119; // -
int ISHL = 120; // -
int LSHL = 121; // -
int ISHR = 122; // -
int LSHR = 123; // -
int IUSHR = 124; // -
int LUSHR = 125; // -
int IAND = 126; // -
int LAND = 127; // -
int IOR = 128; // -
int LOR = 129; // -
int IXOR = 130; // -
int LXOR = 131; // -
int IINC = 132; // visitIincInsn
int I2L = 133; // visitInsn
int I2F = 134; // -
int I2D = 135; // -
int L2I = 136; // -
int L2F = 137; // -
int L2D = 138; // -
int F2I = 139; // -
int F2L = 140; // -
int F2D = 141; // -
int D2I = 142; // -
int D2L = 143; // -
int D2F = 144; // -
int I2B = 145; // -
int I2C = 146; // -
int I2S = 147; // -
int LCMP = 148; // -
int FCMPL = 149; // -
int FCMPG = 150; // -
int DCMPL = 151; // -
int DCMPG = 152; // -
int IFEQ = 153; // visitJumpInsn
int IFNE = 154; // -
int IFLT = 155; // -
int IFGE = 156; // -
int IFGT = 157; // -
int IFLE = 158; // -
int IF_ICMPEQ = 159; // -
int IF_ICMPNE = 160; // -
int IF_ICMPLT = 161; // -
int IF_ICMPGE = 162; // -
int IF_ICMPGT = 163; // -
int IF_ICMPLE = 164; // -
int IF_ACMPEQ = 165; // -
int IF_ACMPNE = 166; // -
int GOTO = 167; // -
int JSR = 168; // -
int RET = 169; // visitVarInsn
int TABLESWITCH = 170; // visiTableSwitchInsn
int LOOKUPSWITCH = 171; // visitLookupSwitch
int IRETURN = 172; // visitInsn
int LRETURN = 173; // -
int FRETURN = 174; // -
int DRETURN = 175; // -
int ARETURN = 176; // -
int RETURN = 177; // -
int GETSTATIC = 178; // visitFieldInsn
int PUTSTATIC = 179; // -
int GETFIELD = 180; // -
int PUTFIELD = 181; // -
int INVOKEVIRTUAL = 182; // visitMethodInsn
int INVOKESPECIAL = 183; // -
int INVOKESTATIC = 184; // -
int INVOKEINTERFACE = 185; // -
int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn
int NEW = 187; // visitTypeInsn
int NEWARRAY = 188; // visitIntInsn
int ANEWARRAY = 189; // visitTypeInsn
int ARRAYLENGTH = 190; // visitInsn
int ATHROW = 191; // -
int CHECKCAST = 192; // visitTypeInsn
int INSTANCEOF = 193; // -
int MONITORENTER = 194; // visitInsn
int MONITOREXIT = 195; // -
// int WIDE = 196; // NOT VISITED
int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn
int IFNULL = 198; // visitJumpInsn
int IFNONNULL = 199; // -
// int GOTO_W = 200; // -
// int JSR_W = 201; // -
}

View File

@@ -0,0 +1,934 @@
/*
* 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;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* A Java field or method type. This class can be used to make it easier to
* manipulate type and method descriptors.
*
* @author Eric Bruneton
* @author Chris Nokleberg
*/
public class Type {
/**
* The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
*/
public static final int VOID = 0;
/**
* The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
*/
public static final int BOOLEAN = 1;
/**
* The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
*/
public static final int CHAR = 2;
/**
* The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
*/
public static final int BYTE = 3;
/**
* The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
*/
public static final int SHORT = 4;
/**
* The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
*/
public static final int INT = 5;
/**
* The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
*/
public static final int FLOAT = 6;
/**
* The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
*/
public static final int LONG = 7;
/**
* The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
*/
public static final int DOUBLE = 8;
/**
* The sort of array reference types. See {@link #getSort getSort}.
*/
public static final int ARRAY = 9;
/**
* The sort of object reference types. See {@link #getSort getSort}.
*/
public static final int OBJECT = 10;
/**
* The sort of method types. See {@link #getSort getSort}.
*/
public static final int METHOD = 11;
/**
* The <tt>void</tt> type.
*/
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1);
/**
* The <tt>boolean</tt> type.
*/
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <tt>char</tt> type.
*/
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1);
/**
* The <tt>byte</tt> type.
*/
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <tt>short</tt> type.
*/
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1);
/**
* The <tt>int</tt> type.
*/
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1);
/**
* The <tt>float</tt> type.
*/
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1);
/**
* The <tt>long</tt> type.
*/
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1);
/**
* The <tt>double</tt> type.
*/
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1);
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
/**
* The sort of this Java type.
*/
private final int sort;
/**
* A buffer containing the internal name of this Java type. This field is
* only used for reference types.
*/
private final char[] buf;
/**
* The offset of the internal name of this Java type in {@link #buf buf} or,
* for primitive types, the size, descriptor and getOpcode offsets for this
* type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset
* for IALOAD or IASTORE, byte 3 the offset for all other instructions).
*/
private final int off;
/**
* The length of the internal name of this Java type.
*/
private final int len;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructs a reference type.
*
* @param sort
* the sort of the reference type to be constructed.
* @param buf
* a buffer containing the descriptor of the previous type.
* @param off
* the offset of this descriptor in the previous buffer.
* @param len
* the length of this descriptor.
*/
private Type(final int sort, final char[] buf, final int off, final int len) {
this.sort = sort;
this.buf = buf;
this.off = off;
this.len = len;
}
/**
* Returns the Java type corresponding to the given type descriptor.
*
* @param typeDescriptor
* a field or method type descriptor.
* @return the Java type corresponding to the given type descriptor.
*/
public static Type getType(final String typeDescriptor) {
return getType(typeDescriptor.toCharArray(), 0);
}
/**
* Returns the Java type corresponding to the given internal name.
*
* @param internalName
* an internal name.
* @return the Java type corresponding to the given internal name.
*/
public static Type getObjectType(final String internalName) {
char[] buf = internalName.toCharArray();
return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length);
}
/**
* Returns the Java type corresponding to the given method descriptor.
* Equivalent to <code>Type.getType(methodDescriptor)</code>.
*
* @param methodDescriptor
* a method descriptor.
* @return the Java type corresponding to the given method descriptor.
*/
public static Type getMethodType(final String methodDescriptor) {
return getType(methodDescriptor.toCharArray(), 0);
}
/**
* Returns the Java method type corresponding to the given argument and
* return types.
*
* @param returnType
* the return type of the method.
* @param argumentTypes
* the argument types of the method.
* @return the Java type corresponding to the given argument and return
* types.
*/
public static Type getMethodType(final Type returnType,
final Type... argumentTypes) {
return getType(getMethodDescriptor(returnType, argumentTypes));
}
/**
* Returns the Java type corresponding to the given class.
*
* @param c
* a class.
* @return the Java type corresponding to the given class.
*/
public static Type getType(final Class<?> c) {
if (c.isPrimitive()) {
if (c == Integer.TYPE) {
return INT_TYPE;
} else if (c == Void.TYPE) {
return VOID_TYPE;
} else if (c == Boolean.TYPE) {
return BOOLEAN_TYPE;
} else if (c == Byte.TYPE) {
return BYTE_TYPE;
} else if (c == Character.TYPE) {
return CHAR_TYPE;
} else if (c == Short.TYPE) {
return SHORT_TYPE;
} else if (c == Double.TYPE) {
return DOUBLE_TYPE;
} else if (c == Float.TYPE) {
return FLOAT_TYPE;
} else /* if (c == Long.TYPE) */{
return LONG_TYPE;
}
} else {
return getType(getDescriptor(c));
}
}
/**
* Returns the Java method type corresponding to the given constructor.
*
* @param c
* a {@link Constructor Constructor} object.
* @return the Java method type corresponding to the given constructor.
*/
public static Type getType(final Constructor<?> c) {
return getType(getConstructorDescriptor(c));
}
/**
* Returns the Java method type corresponding to the given method.
*
* @param m
* a {@link Method Method} object.
* @return the Java method type corresponding to the given method.
*/
public static Type getType(final Method m) {
return getType(getMethodDescriptor(m));
}
/**
* Returns the Java types corresponding to the argument types of the given
* method descriptor.
*
* @param methodDescriptor
* a method descriptor.
* @return the Java types corresponding to the argument types of the given
* method descriptor.
*/
public static Type[] getArgumentTypes(final String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
int off = 1;
int size = 0;
while (true) {
char car = buf[off++];
if (car == ')') {
break;
} else if (car == 'L') {
while (buf[off++] != ';') {
}
++size;
} else if (car != '[') {
++size;
}
}
Type[] args = new Type[size];
off = 1;
size = 0;
while (buf[off] != ')') {
args[size] = getType(buf, off);
off += args[size].len + (args[size].sort == OBJECT ? 2 : 0);
size += 1;
}
return args;
}
/**
* Returns the Java types corresponding to the argument types of the given
* method.
*
* @param method
* a method.
* @return the Java types corresponding to the argument types of the given
* method.
*/
public static Type[] getArgumentTypes(final Method method) {
Class<?>[] classes = method.getParameterTypes();
Type[] types = new Type[classes.length];
for (int i = classes.length - 1; i >= 0; --i) {
types[i] = getType(classes[i]);
}
return types;
}
/**
* Returns the Java type corresponding to the return type of the given
* method descriptor.
*
* @param methodDescriptor
* a method descriptor.
* @return the Java type corresponding to the return type of the given
* method descriptor.
*/
public static Type getReturnType(final String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
int off = 1;
while (true) {
char car = buf[off++];
if (car == ')') {
return getType(buf, off);
} else if (car == 'L') {
while (buf[off++] != ';') {
}
}
}
}
/**
* Returns the Java type corresponding to the return type of the given
* method.
*
* @param method
* a method.
* @return the Java type corresponding to the return type of the given
* method.
*/
public static Type getReturnType(final Method method) {
return getType(method.getReturnType());
}
/**
* Computes the size of the arguments and of the return value of a method.
*
* @param desc
* the descriptor of a method.
* @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i =
* <tt>(argSize &lt;&lt; 2) | retSize</tt> (argSize is therefore equal to
* <tt>i &gt;&gt; 2</tt>, and retSize to <tt>i &amp; 0x03</tt>).
*/
public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1;
int c = 1;
while (true) {
char car = desc.charAt(c++);
if (car == ')') {
car = desc.charAt(c);
return n << 2
| (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
} else if (car == 'L') {
while (desc.charAt(c++) != ';') {
}
n += 1;
} else if (car == '[') {
while ((car = desc.charAt(c)) == '[') {
++c;
}
if (car == 'D' || car == 'J') {
n -= 1;
}
} else if (car == 'D' || car == 'J') {
n += 2;
} else {
n += 1;
}
}
}
/**
* Returns the Java type corresponding to the given type descriptor. For
* method descriptors, buf is supposed to contain nothing more than the
* descriptor itself.
*
* @param buf
* a buffer containing a type descriptor.
* @param off
* the offset of this descriptor in the previous buffer.
* @return the Java type corresponding to the given type descriptor.
*/
private static Type getType(final char[] buf, final int off) {
int len;
switch (buf[off]) {
case 'V':
return VOID_TYPE;
case 'Z':
return BOOLEAN_TYPE;
case 'C':
return CHAR_TYPE;
case 'B':
return BYTE_TYPE;
case 'S':
return SHORT_TYPE;
case 'I':
return INT_TYPE;
case 'F':
return FLOAT_TYPE;
case 'J':
return LONG_TYPE;
case 'D':
return DOUBLE_TYPE;
case '[':
len = 1;
while (buf[off + len] == '[') {
++len;
}
if (buf[off + len] == 'L') {
++len;
while (buf[off + len] != ';') {
++len;
}
}
return new Type(ARRAY, buf, off, len + 1);
case 'L':
len = 1;
while (buf[off + len] != ';') {
++len;
}
return new Type(OBJECT, buf, off + 1, len - 1);
// case '(':
default:
return new Type(METHOD, buf, off, buf.length - off);
}
}
// ------------------------------------------------------------------------
// Accessors
// ------------------------------------------------------------------------
/**
* Returns the sort of this Java type.
*
* @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR},
* {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT},
* {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE},
* {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD
* METHOD}.
*/
public int getSort() {
return sort;
}
/**
* Returns the number of dimensions of this array type. This method should
* only be used for an array type.
*
* @return the number of dimensions of this array type.
*/
public int getDimensions() {
int i = 1;
while (buf[off + i] == '[') {
++i;
}
return i;
}
/**
* Returns the type of the elements of this array type. This method should
* only be used for an array type.
*
* @return Returns the type of the elements of this array type.
*/
public Type getElementType() {
return getType(buf, off + getDimensions());
}
/**
* Returns the binary name of the class corresponding to this type. This
* method must not be used on method types.
*
* @return the binary name of the class corresponding to this type.
*/
public String getClassName() {
switch (sort) {
case VOID:
return "void";
case BOOLEAN:
return "boolean";
case CHAR:
return "char";
case BYTE:
return "byte";
case SHORT:
return "short";
case INT:
return "int";
case FLOAT:
return "float";
case LONG:
return "long";
case DOUBLE:
return "double";
case ARRAY:
StringBuilder sb = new StringBuilder(getElementType().getClassName());
for (int i = getDimensions(); i > 0; --i) {
sb.append("[]");
}
return sb.toString();
case OBJECT:
return new String(buf, off, len).replace('/', '.');
default:
return null;
}
}
/**
* Returns the internal name of the class corresponding to this object or
* array type. The internal name of a class is its fully qualified name (as
* returned by Class.getName(), where '.' are replaced by '/'. This method
* should only be used for an object or array type.
*
* @return the internal name of the class corresponding to this object type.
*/
public String getInternalName() {
return new String(buf, off, len);
}
/**
* Returns the argument types of methods of this type. This method should
* only be used for method types.
*
* @return the argument types of methods of this type.
*/
public Type[] getArgumentTypes() {
return getArgumentTypes(getDescriptor());
}
/**
* Returns the return type of methods of this type. This method should only
* be used for method types.
*
* @return the return type of methods of this type.
*/
public Type getReturnType() {
return getReturnType(getDescriptor());
}
/**
* Returns the size of the arguments and of the return value of methods of
* this type. This method should only be used for method types.
*
* @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize,
* packed into a single
* int i = <tt>(argSize &lt;&lt; 2) | retSize</tt>
* (argSize is therefore equal to <tt>i &gt;&gt; 2</tt>,
* and retSize to <tt>i &amp; 0x03</tt>).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
}
// ------------------------------------------------------------------------
// Conversion to type descriptors
// ------------------------------------------------------------------------
/**
* Returns the descriptor corresponding to this Java type.
*
* @return the descriptor corresponding to this Java type.
*/
public String getDescriptor() {
StringBuilder buf = new StringBuilder();
getDescriptor(buf);
return buf.toString();
}
/**
* Returns the descriptor corresponding to the given argument and return
* types.
*
* @param returnType
* the return type of the method.
* @param argumentTypes
* the argument types of the method.
* @return the descriptor corresponding to the given argument and return
* types.
*/
public static String getMethodDescriptor(final Type returnType,
final Type... argumentTypes) {
StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < argumentTypes.length; ++i) {
argumentTypes[i].getDescriptor(buf);
}
buf.append(')');
returnType.getDescriptor(buf);
return buf.toString();
}
/**
* Appends the descriptor corresponding to this Java type to the given
* string buffer.
*
* @param buf
* the string buffer to which the descriptor must be appended.
*/
private void getDescriptor(final StringBuilder buf) {
if (this.buf == null) {
// descriptor is in byte 3 of 'off' for primitive types (buf ==
// null)
buf.append((char) ((off & 0xFF000000) >>> 24));
} else if (sort == OBJECT) {
buf.append('L');
buf.append(this.buf, off, len);
buf.append(';');
} else { // sort == ARRAY || sort == METHOD
buf.append(this.buf, off, len);
}
}
// ------------------------------------------------------------------------
// Direct conversion from classes to type descriptors,
// without intermediate Type objects
// ------------------------------------------------------------------------
/**
* Returns the internal name of the given class. The internal name of a
* class is its fully qualified name, as returned by Class.getName(), where
* '.' are replaced by '/'.
*
* @param c
* an object or array class.
* @return the internal name of the given class.
*/
public static String getInternalName(final Class<?> c) {
return c.getName().replace('.', '/');
}
/**
* Returns the descriptor corresponding to the given Java type.
*
* @param c
* an object class, a primitive class or an array class.
* @return the descriptor corresponding to the given class.
*/
public static String getDescriptor(final Class<?> c) {
StringBuilder buf = new StringBuilder();
getDescriptor(buf, c);
return buf.toString();
}
/**
* Returns the descriptor corresponding to the given constructor.
*
* @param c
* a {@link Constructor Constructor} object.
* @return the descriptor of the given constructor.
*/
public static String getConstructorDescriptor(final Constructor<?> c) {
Class<?>[] parameters = c.getParameterTypes();
StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
getDescriptor(buf, parameters[i]);
}
return buf.append(")V").toString();
}
/**
* Returns the descriptor corresponding to the given method.
*
* @param m
* a {@link Method Method} object.
* @return the descriptor of the given method.
*/
public static String getMethodDescriptor(final Method m) {
Class<?>[] parameters = m.getParameterTypes();
StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
getDescriptor(buf, parameters[i]);
}
buf.append(')');
getDescriptor(buf, m.getReturnType());
return buf.toString();
}
/**
* Appends the descriptor of the given class to the given string buffer.
*
* @param buf
* the string buffer to which the descriptor must be appended.
* @param c
* the class whose descriptor must be computed.
*/
private static void getDescriptor(final StringBuilder buf, final Class<?> c) {
Class<?> d = c;
while (true) {
if (d.isPrimitive()) {
char car;
if (d == Integer.TYPE) {
car = 'I';
} else if (d == Void.TYPE) {
car = 'V';
} else if (d == Boolean.TYPE) {
car = 'Z';
} else if (d == Byte.TYPE) {
car = 'B';
} else if (d == Character.TYPE) {
car = 'C';
} else if (d == Short.TYPE) {
car = 'S';
} else if (d == Double.TYPE) {
car = 'D';
} else if (d == Float.TYPE) {
car = 'F';
} else /* if (d == Long.TYPE) */{
car = 'J';
}
buf.append(car);
return;
} else if (d.isArray()) {
buf.append('[');
d = d.getComponentType();
} else {
buf.append('L');
String name = d.getName();
int len = name.length();
for (int i = 0; i < len; ++i) {
char car = name.charAt(i);
buf.append(car == '.' ? '/' : car);
}
buf.append(';');
return;
}
}
}
// ------------------------------------------------------------------------
// Corresponding size and opcodes
// ------------------------------------------------------------------------
/**
* Returns the size of values of this type. This method must not be used for
* method types.
*
* @return the size of values of this type, i.e., 2 for <tt>long</tt> and
* <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
*/
public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null)
return buf == null ? (off & 0xFF) : 1;
}
/**
* Returns a JVM instruction opcode adapted to this Java type. This method
* must not be used for method types.
*
* @param opcode
* a JVM instruction opcode. This opcode must be one of ILOAD,
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
* @return an opcode that is similar to the given opcode, but adapted to
* this Java type. For example, if this type is <tt>float</tt> and
* <tt>opcode</tt> is IRETURN, this method returns FRETURN.
*/
public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
// the offset for IALOAD or IASTORE is in byte 1 of 'off' for
// primitive types (buf == null)
return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4);
} else {
// the offset for other instructions is in byte 2 of 'off' for
// primitive types (buf == null)
return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4);
}
}
// ------------------------------------------------------------------------
// Equals, hashCode and toString
// ------------------------------------------------------------------------
/**
* Tests if the given object is equal to this type.
*
* @param o
* the object to be compared to this type.
* @return <tt>true</tt> if the given object is equal to this type.
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Type)) {
return false;
}
Type t = (Type) o;
if (sort != t.sort) {
return false;
}
if (sort >= ARRAY) {
if (len != t.len) {
return false;
}
for (int i = off, j = t.off, end = i + len; i < end; i++, j++) {
if (buf[i] != t.buf[j]) {
return false;
}
}
}
return true;
}
/**
* Returns a hash code value for this type.
*
* @return a hash code value for this type.
*/
@Override
public int hashCode() {
int hc = 13 * sort;
if (sort >= ARRAY) {
for (int i = off, end = i + len; i < end; i++) {
hc = 17 * (hc + buf[i]);
}
}
return hc;
}
/**
* Returns a string representation of this type.
*
* @return the descriptor of this type.
*/
@Override
public String toString() {
return getDescriptor();
}
}

View File

@@ -0,0 +1,225 @@
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2013 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.redkale.asm;
/**
* The path to a type argument, wildcard bound, array element type, or static
* inner type within an enclosing type.
*
* @author Eric Bruneton
*/
public class TypePath {
/**
* A type path step that steps into the element type of an array type. See
* {@link #getStep getStep}.
*/
public final static int ARRAY_ELEMENT = 0;
/**
* A type path step that steps into the nested type of a class type. See
* {@link #getStep getStep}.
*/
public final static int INNER_TYPE = 1;
/**
* A type path step that steps into the bound of a wildcard type. See
* {@link #getStep getStep}.
*/
public final static int WILDCARD_BOUND = 2;
/**
* A type path step that steps into a type argument of a generic type. See
* {@link #getStep getStep}.
*/
public final static int TYPE_ARGUMENT = 3;
/**
* The byte array where the path is stored, in Java class file format.
*/
byte[] b;
/**
* The offset of the first byte of the type path in 'b'.
*/
int offset;
/**
* Creates a new type path.
*
* @param b
* the byte array containing the type path in Java class file
* format.
* @param offset
* the offset of the first byte of the type path in 'b'.
*/
TypePath(byte[] b, int offset) {
this.b = b;
this.offset = offset;
}
/**
* Returns the length of this path.
*
* @return the length of this path.
*/
public int getLength() {
return b[offset];
}
/**
* Returns the value of the given step of this path.
*
* @param index
* an index between 0 and {@link #getLength()}, exclusive.
* @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE
* INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or
* {@link #TYPE_ARGUMENT TYPE_ARGUMENT}.
*/
public int getStep(int index) {
return b[offset + 2 * index + 1];
}
/**
* Returns the index of the type argument that the given step is stepping
* into. This method should only be used for steps whose value is
* {@link #TYPE_ARGUMENT TYPE_ARGUMENT}.
*
* @param index
* an index between 0 and {@link #getLength()}, exclusive.
* @return the index of the type argument that the given step is stepping
* into.
*/
public int getStepArgument(int index) {
return b[offset + 2 * index + 2];
}
/**
* Converts a type path in string form, in the format used by
* {@link #toString()}, into a TypePath object.
*
* @param typePath
* a type path in string form, in the format used by
* {@link #toString()}. May be null or empty.
* @return the corresponding TypePath object, or null if the path is empty.
*/
public static TypePath fromString(final String typePath) {
if (typePath == null || typePath.length() == 0) {
return null;
}
int n = typePath.length();
ByteVector out = new ByteVector(n);
out.putByte(0);
for (int i = 0; i < n;) {
char c = typePath.charAt(i++);
if (c == '[') {
out.put11(ARRAY_ELEMENT, 0);
} else if (c == '.') {
out.put11(INNER_TYPE, 0);
} else if (c == '*') {
out.put11(WILDCARD_BOUND, 0);
} else if (c >= '0' && c <= '9') {
int typeArg = c - '0';
while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') {
typeArg = typeArg * 10 + c - '0';
i += 1;
}
if (i < n && typePath.charAt(i) == ';') {
i += 1;
}
out.put11(TYPE_ARGUMENT, typeArg);
}
}
out.data[0] = (byte) (out.length / 2);
return new TypePath(out.data, 0);
}
/**
* Returns a string representation of this type path. {@link #ARRAY_ELEMENT
* ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE
* INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps
* with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type
* argument index in decimal form followed by ';'.
*/
@Override
public String toString() {
int length = getLength();
StringBuilder result = new StringBuilder(length * 2);
for (int i = 0; i < length; ++i) {
switch (getStep(i)) {
case ARRAY_ELEMENT:
result.append('[');
break;
case INNER_TYPE:
result.append('.');
break;
case WILDCARD_BOUND:
result.append('*');
break;
case TYPE_ARGUMENT:
result.append(getStepArgument(i)).append(';');
break;
default:
result.append('_');
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,481 @@
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2013 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.redkale.asm;
/**
* A reference to a type appearing in a class, field or method declaration, or
* on an instruction. Such a reference designates the part of the class where
* the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws'
* clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable
* declaration, etc).
*
* @author Eric Bruneton
*/
public class TypeReference {
/**
* The sort of type references that target a type parameter of a generic
* class. See {@link #getSort getSort}.
*/
public final static int CLASS_TYPE_PARAMETER = 0x00;
/**
* The sort of type references that target a type parameter of a generic
* method. See {@link #getSort getSort}.
*/
public final static int METHOD_TYPE_PARAMETER = 0x01;
/**
* The sort of type references that target the super class of a class or one
* of the interfaces it implements. See {@link #getSort getSort}.
*/
public final static int CLASS_EXTENDS = 0x10;
/**
* The sort of type references that target a bound of a type parameter of a
* generic class. See {@link #getSort getSort}.
*/
public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11;
/**
* The sort of type references that target a bound of a type parameter of a
* generic method. See {@link #getSort getSort}.
*/
public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12;
/**
* The sort of type references that target the type of a field. See
* {@link #getSort getSort}.
*/
public final static int FIELD = 0x13;
/**
* The sort of type references that target the return type of a method. See
* {@link #getSort getSort}.
*/
public final static int METHOD_RETURN = 0x14;
/**
* The sort of type references that target the receiver type of a method.
* See {@link #getSort getSort}.
*/
public final static int METHOD_RECEIVER = 0x15;
/**
* The sort of type references that target the type of a formal parameter of
* a method. See {@link #getSort getSort}.
*/
public final static int METHOD_FORMAL_PARAMETER = 0x16;
/**
* The sort of type references that target the type of an exception declared
* in the throws clause of a method. See {@link #getSort getSort}.
*/
public final static int THROWS = 0x17;
/**
* The sort of type references that target the type of a local variable in a
* method. See {@link #getSort getSort}.
*/
public final static int LOCAL_VARIABLE = 0x40;
/**
* The sort of type references that target the type of a resource variable
* in a method. See {@link #getSort getSort}.
*/
public final static int RESOURCE_VARIABLE = 0x41;
/**
* The sort of type references that target the type of the exception of a
* 'catch' clause in a method. See {@link #getSort getSort}.
*/
public final static int EXCEPTION_PARAMETER = 0x42;
/**
* The sort of type references that target the type declared in an
* 'instanceof' instruction. See {@link #getSort getSort}.
*/
public final static int INSTANCEOF = 0x43;
/**
* The sort of type references that target the type of the object created by
* a 'new' instruction. See {@link #getSort getSort}.
*/
public final static int NEW = 0x44;
/**
* The sort of type references that target the receiver type of a
* constructor reference. See {@link #getSort getSort}.
*/
public final static int CONSTRUCTOR_REFERENCE = 0x45;
/**
* The sort of type references that target the receiver type of a method
* reference. See {@link #getSort getSort}.
*/
public final static int METHOD_REFERENCE = 0x46;
/**
* The sort of type references that target the type declared in an explicit
* or implicit cast instruction. See {@link #getSort getSort}.
*/
public final static int CAST = 0x47;
/**
* The sort of type references that target a type parameter of a generic
* constructor in a constructor call. See {@link #getSort getSort}.
*/
public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48;
/**
* The sort of type references that target a type parameter of a generic
* method in a method call. See {@link #getSort getSort}.
*/
public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49;
/**
* The sort of type references that target a type parameter of a generic
* constructor in a constructor reference. See {@link #getSort getSort}.
*/
public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A;
/**
* The sort of type references that target a type parameter of a generic
* method in a method reference. See {@link #getSort getSort}.
*/
public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B;
/**
* The type reference value in Java class file format.
*/
private int value;
/**
* Creates a new TypeReference.
*
* @param typeRef
* the int encoded value of the type reference, as received in a
* visit method related to type annotations, like
* visitTypeAnnotation.
*/
public TypeReference(int typeRef) {
this.value = typeRef;
}
/**
* Returns a type reference of the given sort.
*
* @param sort
* {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN},
* {@link #METHOD_RECEIVER METHOD_RECEIVER},
* {@link #LOCAL_VARIABLE LOCAL_VARIABLE},
* {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE},
* {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW},
* {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or
* {@link #METHOD_REFERENCE METHOD_REFERENCE}.
* @return a type reference of the given sort.
*/
public static TypeReference newTypeReference(int sort) {
return new TypeReference(sort << 24);
}
/**
* Returns a reference to a type parameter of a generic class or method.
*
* @param sort
* {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or
* {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}.
* @param paramIndex
* the type parameter index.
* @return a reference to the given generic class or method type parameter.
*/
public static TypeReference newTypeParameterReference(int sort,
int paramIndex) {
return new TypeReference((sort << 24) | (paramIndex << 16));
}
/**
* Returns a reference to a type parameter bound of a generic class or
* method.
*
* @param sort
* {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or
* {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}.
* @param paramIndex
* the type parameter index.
* @param boundIndex
* the type bound index within the above type parameters.
* @return a reference to the given generic class or method type parameter
* bound.
*/
public static TypeReference newTypeParameterBoundReference(int sort,
int paramIndex, int boundIndex) {
return new TypeReference((sort << 24) | (paramIndex << 16)
| (boundIndex << 8));
}
/**
* Returns a reference to the super class or to an interface of the
* 'implements' clause of a class.
*
* @param itfIndex
* the index of an interface in the 'implements' clause of a
* class, or -1 to reference the super class of the class.
* @return a reference to the given super type of a class.
*/
public static TypeReference newSuperTypeReference(int itfIndex) {
itfIndex &= 0xFFFF;
return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8));
}
/**
* Returns a reference to the type of a formal parameter of a method.
*
* @param paramIndex
* the formal parameter index.
*
* @return a reference to the type of the given method formal parameter.
*/
public static TypeReference newFormalParameterReference(int paramIndex) {
return new TypeReference((METHOD_FORMAL_PARAMETER << 24)
| (paramIndex << 16));
}
/**
* Returns a reference to the type of an exception, in a 'throws' clause of
* a method.
*
* @param exceptionIndex
* the index of an exception in a 'throws' clause of a method.
*
* @return a reference to the type of the given exception.
*/
public static TypeReference newExceptionReference(int exceptionIndex) {
return new TypeReference((THROWS << 24) | (exceptionIndex << 8));
}
/**
* Returns a reference to the type of the exception declared in a 'catch'
* clause of a method.
*
* @param tryCatchBlockIndex
* the index of a try catch block (using the order in which they
* are visited with visitTryCatchBlock).
*
* @return a reference to the type of the given exception.
*/
public static TypeReference newTryCatchReference(int tryCatchBlockIndex) {
return new TypeReference((EXCEPTION_PARAMETER << 24)
| (tryCatchBlockIndex << 8));
}
/**
* Returns a reference to the type of a type argument in a constructor or
* method call or reference.
*
* @param sort
* {@link #CAST CAST},
* {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
* CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
* {@link #METHOD_INVOCATION_TYPE_ARGUMENT
* METHOD_INVOCATION_TYPE_ARGUMENT},
* {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
* CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
* {@link #METHOD_REFERENCE_TYPE_ARGUMENT
* METHOD_REFERENCE_TYPE_ARGUMENT}.
* @param argIndex
* the type argument index.
*
* @return a reference to the type of the given type argument.
*/
public static TypeReference newTypeArgumentReference(int sort, int argIndex) {
return new TypeReference((sort << 24) | argIndex);
}
/**
* Returns the sort of this type reference.
*
* @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER},
* {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER},
* {@link #CLASS_EXTENDS CLASS_EXTENDS},
* {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND},
* {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND},
* {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN},
* {@link #METHOD_RECEIVER METHOD_RECEIVER},
* {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER},
* {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE},
* {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE},
* {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER},
* {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW},
* {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE},
* {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST},
* {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
* CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
* {@link #METHOD_INVOCATION_TYPE_ARGUMENT
* METHOD_INVOCATION_TYPE_ARGUMENT},
* {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
* CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
* {@link #METHOD_REFERENCE_TYPE_ARGUMENT
* METHOD_REFERENCE_TYPE_ARGUMENT}.
*/
public int getSort() {
return value >>> 24;
}
/**
* Returns the index of the type parameter referenced by this type
* reference. This method must only be used for type references whose sort
* is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER},
* {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER},
* {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or
* {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}.
*
* @return a type parameter index.
*/
public int getTypeParameterIndex() {
return (value & 0x00FF0000) >> 16;
}
/**
* Returns the index of the type parameter bound, within the type parameter
* {@link #getTypeParameterIndex}, referenced by this type reference. This
* method must only be used for type references whose sort is
* {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or
* {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}.
*
* @return a type parameter bound index.
*/
public int getTypeParameterBoundIndex() {
return (value & 0x0000FF00) >> 8;
}
/**
* Returns the index of the "super type" of a class that is referenced by
* this type reference. This method must only be used for type references
* whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}.
*
* @return the index of an interface in the 'implements' clause of a class,
* or -1 if this type reference references the type of the super
* class.
*/
public int getSuperTypeIndex() {
return (short) ((value & 0x00FFFF00) >> 8);
}
/**
* Returns the index of the formal parameter whose type is referenced by
* this type reference. This method must only be used for type references
* whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}.
*
* @return a formal parameter index.
*/
public int getFormalParameterIndex() {
return (value & 0x00FF0000) >> 16;
}
/**
* Returns the index of the exception, in a 'throws' clause of a method,
* whose type is referenced by this type reference. This method must only be
* used for type references whose sort is {@link #THROWS THROWS}.
*
* @return the index of an exception in the 'throws' clause of a method.
*/
public int getExceptionIndex() {
return (value & 0x00FFFF00) >> 8;
}
/**
* Returns the index of the try catch block (using the order in which they
* are visited with visitTryCatchBlock), whose 'catch' type is referenced by
* this type reference. This method must only be used for type references
* whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} .
*
* @return the index of an exception in the 'throws' clause of a method.
*/
public int getTryCatchBlockIndex() {
return (value & 0x00FFFF00) >> 8;
}
/**
* Returns the index of the type argument referenced by this type reference.
* This method must only be used for type references whose sort is
* {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
* CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
* {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT},
* {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
* CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
* {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}.
*
* @return a type parameter index.
*/
public int getTypeArgumentIndex() {
return value & 0xFF;
}
/**
* Returns the int encoded value of this type reference, suitable for use in
* visit methods related to type annotations, like visitTypeAnnotation.
*
* @return the int encoded value of this type reference.
*/
public int getValue() {
return value;
}
}

View File

@@ -24,3 +24,4 @@ ModuleWriter.java
Opcodes.java
Type.java
TypePath.java
TypeReference.java

View File

@@ -0,0 +1,4 @@
/**
* 本包下所有代码均是从/java.base/jdk/internal/org/objectweb/asm 拷贝过来的
*/
package org.redkale.asm;

View File

@@ -18,6 +18,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert;
@@ -44,8 +45,6 @@ import org.w3c.dom.*;
* 4、最后进行Service、Servlet与其他资源之间的依赖注入
* </pre>
* <p>
* 编译时需要加入: -XDignore.symbol.file=true
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
@@ -92,6 +91,16 @@ public final class Application {
*/
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
/**
* 当前Server的线程池
*/
public static final String RESNAME_SERVER_EXECUTOR = Server.RESNAME_SERVER_EXECUTOR;
/**
* 当前Server的ResourceFactory
*/
public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY;
//本地IP地址
final InetAddress localAddress;
@@ -243,8 +252,10 @@ public final class Application {
AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
int bufferCapacity = 8 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 16;
int bufferCapacity = 32 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) {
@@ -253,9 +264,11 @@ public final class Application {
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
if (transportConf != null) {
//--------------transportBufferPool-----------
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 4 * 1024);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 8 * 1024);
readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds);
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
final int capacity = bufferCapacity;
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
@@ -267,7 +280,7 @@ public final class Application {
try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).newInstance();
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).getDeclaredConstructor().newInstance();
}
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
@@ -280,7 +293,7 @@ public final class Application {
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
}
}
if (transportGroup == null) {
@@ -306,8 +319,10 @@ public final class Application {
return true;
});
}
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"));
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.poolmaxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30"));
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
@@ -346,7 +361,11 @@ public final class Application {
}
public void init() throws Exception {
System.setProperty("sun.nio.ch.internalThreadPoolSize", "" + Runtime.getRuntime().availableProcessors() * 4);
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
System.setProperty("net.transport.poolmaxconns", "100");
System.setProperty("net.transport.pinginterval", "30");
System.setProperty("net.transport.checkinterval", "30");
System.setProperty("convert.bson.tiny", "true");
System.setProperty("convert.json.tiny", "true");
System.setProperty("convert.bson.pool.size", "128");
@@ -372,7 +391,6 @@ public final class Application {
if (dfloads != null) {
for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue;
dfload = dfload.trim().replace("${APP_HOME}", home.getCanonicalPath()).replace('\\', '/');
final File df = (dfload.indexOf('/') < 0) ? new File(home, "conf/" + dfload) : new File(dfload);
if (df.isFile()) {
Properties ps = new Properties();
@@ -387,7 +405,6 @@ public final class Application {
String name = prop.getValue("name");
String value = prop.getValue("value");
if (name == null || value == null) continue;
value = value.replace("${APP_HOME}", home.getCanonicalPath()).replace('\\', '/');
if (name.startsWith("system.property.")) {
System.setProperty(name.substring("system.property.".length()), value);
} else if (name.startsWith("mimetype.property.")) {
@@ -420,7 +437,8 @@ public final class Application {
if (type == Application.class) {
field.set(src, application);
} else if (type == ResourceFactory.class) {
field.set(src, res.name().equalsIgnoreCase("server") ? rf : (res.name().isEmpty() ? application.resourceFactory : null));
boolean serv = RESNAME_SERVER_RESFACTORY.equals(res.name()) || res.name().equalsIgnoreCase("server");
field.set(src, serv ? rf : (res.name().isEmpty() ? application.resourceFactory : null));
} else if (type == TransportFactory.class) {
field.set(src, application.sncpTransportFactory);
} else if (type == NodeSncpServer.class) {
@@ -493,9 +511,10 @@ public final class Application {
for (AnyValue conf : resources.getAnyValues("listener")) {
final String listenClass = conf.getValue("value", "");
if (listenClass.isEmpty()) continue;
Class clazz = Class.forName(listenClass);
Class clazz = classLoader.loadClass(listenClass);
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
ApplicationListener listener = (ApplicationListener) clazz.newInstance();
@SuppressWarnings("unchecked")
ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance();
listener.init(config);
this.listeners.add(listener);
}
@@ -587,15 +606,15 @@ public final class Application {
buffer.flip();
channel.write(buffer);
buffer.clear();
channel.configureBlocking(false);
channel.configureBlocking(true);
try {
channel.read(buffer);
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
channel.close();
logger.info(new String(bytes));
Thread.sleep(500);
logger.info("Send: " + command + ", Reply: " + new String(bytes));
Thread.sleep(1000);
} catch (Exception e) {
if (e instanceof PortUnreachableException) {
if ("APIDOC".equalsIgnoreCase(command)) {
@@ -775,7 +794,7 @@ public final class Application {
}
public static Application create(final boolean singleton) throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath();
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home);
File appfile = new File(home, "conf/application.xml");
return new Application(singleton, load(new FileInputStream(appfile)));

View File

@@ -14,6 +14,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import java.util.logging.Formatter;
import java.util.regex.Pattern;
/**
* 自定义的日志输出类
@@ -99,6 +100,8 @@ public class LogFileHandler extends Handler {
private boolean append;
private Pattern denyreg;
private final AtomicLong loglength = new AtomicLong();
private final AtomicLong logunusuallength = new AtomicLong();
@@ -154,6 +157,8 @@ public class LogFileHandler extends Handler {
if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
}
Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
} else {
if (logfile.exists() && logfile.length() < 1) logfile.delete();
}
logstream = null;
}
@@ -166,6 +171,8 @@ public class LogFileHandler extends Handler {
if (greater.exists()) Files.move(greater.toPath(), new File(logunusualfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
}
Files.move(logunusualfile.toPath(), new File(logunusualfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
} else {
if (logunusualfile.exists() && logunusualfile.length() < 1) logunusualfile.delete();
}
logunusualstream = null;
}
@@ -259,7 +266,7 @@ public class LogFileHandler extends Handler {
try {
if (filterstr != null) {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
setFilter((Filter) clz.newInstance());
setFilter((Filter) clz.getDeclaredConstructor().newInstance());
}
} catch (Exception e) {
}
@@ -267,7 +274,7 @@ public class LogFileHandler extends Handler {
try {
if (formatterstr != null) {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
setFormatter((Formatter) clz.newInstance());
setFormatter((Formatter) clz.getDeclaredConstructor().newInstance());
}
} catch (Exception e) {
}
@@ -278,6 +285,14 @@ public class LogFileHandler extends Handler {
if (encodingstr != null) setEncoding(encodingstr);
} catch (Exception e) {
}
String denyregstr = manager.getProperty(cname + ".denyreg");
try {
if (denyregstr != null && !denyregstr.trim().isEmpty()) {
denyreg = Pattern.compile(denyregstr);
}
} catch (Exception e) {
}
}
@Override
@@ -294,6 +309,7 @@ public class LogFileHandler extends Handler {
} else {
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
}
if (denyreg != null && denyreg.matcher(record.getMessage()).find()) return;
records.offer(record);
}

View File

@@ -43,7 +43,7 @@ public class NodeHttpServer extends NodeServer {
}
private static Server createServer(Application application, AnyValue serconf) {
return new HttpServer(application.getStartTime());
return new HttpServer(application.getStartTime(), application.getResourceFactory().createChild());
}
public HttpServer getHttpServer() {
@@ -56,16 +56,19 @@ public class NodeHttpServer extends NodeServer {
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Service> createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Filter> createFilterClassFilter() {
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Servlet> createServletClassFilter() {
return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
}
@@ -87,6 +90,7 @@ public class NodeHttpServer extends NodeServer {
}
@Override
@SuppressWarnings("unchecked")
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
}
@@ -119,8 +123,8 @@ public class NodeHttpServer extends NodeServer {
regFactory.register(resourceName, WebSocketNode.class, nodeService);
}
resourceFactory.inject(nodeService, self);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
field.set(src, nodeService);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
@@ -136,7 +140,7 @@ public class NodeHttpServer extends NodeServer {
for (FilterEntry<? extends Filter> en : list) {
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
final HttpFilter filter = clazz.newInstance();
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpFilter(filter, filterConf);
@@ -172,7 +176,7 @@ public class NodeHttpServer extends NodeServer {
if (Modifier.isAbstract(clazz.getModifiers())) continue;
WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null || ws.value().length == 0) continue;
final HttpServlet servlet = clazz.newInstance();
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(servlet, this);
final String[] mappings = ws.value();
String pref = ws.repair() ? prefix : "";
@@ -312,7 +316,7 @@ public class NodeHttpServer extends NodeServer {
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " " + stype.getName() + " create RestWebSocketServlet " + servlet);
if (finest) logger.finest(threadName + " " + stype.getName() + " create a RestWebSocketServlet");
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {

View File

@@ -19,10 +19,9 @@ import javax.annotation.*;
import javax.persistence.Transient;
import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.bson.*;
import org.redkale.net.Filter;
import org.redkale.net.*;
import org.redkale.net.http.WebSocketServlet;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.source.*;
@@ -90,12 +89,13 @@ public abstract class NodeServer {
public NodeServer(Application application, Server server) {
this.application = application;
this.resourceFactory = application.getResourceFactory().createChild();
this.server = server;
this.resourceFactory = server.getResourceFactory();
this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
Thread.currentThread().setContextClassLoader(this.serverClassLoader);
this.serverThread = Thread.currentThread();
this.server.setServerClassLoader(serverClassLoader);
}
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
@@ -135,17 +135,21 @@ public abstract class NodeServer {
resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
//加入指定的classpath
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", ""));
this.serverThread.setContextClassLoader(this.serverClassLoader);
}
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf);
//init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getExecutor());
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", "");
if (!interceptorClass.isEmpty()) {
Class clazz = serverClassLoader.loadClass(interceptorClass);
this.interceptor = (NodeInterceptor) clazz.newInstance();
this.interceptor = (NodeInterceptor) clazz.getDeclaredConstructor().newInstance();
}
ClassFilter<Service> serviceFilter = createServiceClassFilter();
@@ -179,6 +183,7 @@ public abstract class NodeServer {
for (AnyValue sourceConf : resources.getAnyValues("source")) {
try {
Class type = serverClassLoader.loadClass(sourceConf.getValue("value"));
if (type == DataSource.class) type = DataJdbcSource.class;
if (!Service.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf);
} else if (CacheSource.class.isAssignableFrom(type)) {
@@ -193,7 +198,7 @@ public abstract class NodeServer {
}
}
}
//------------------------------------- 注册Resource --------------------------------------------------------
//------------------------------------- 注册 Resource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
Resource res = field.getAnnotation(Resource.class);
@@ -216,7 +221,7 @@ public abstract class NodeServer {
}
}, AnyValue.class, AnyValue[].class);
//------------------------------------- 注册DataSource --------------------------------------------------------
//------------------------------------- 注册 DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
@@ -226,7 +231,14 @@ public abstract class NodeServer {
boolean needinit = true;
if (sourceConf != null) {
final Class sourceType = serverClassLoader.loadClass(sourceConf.getValue("value"));
if (DataSource.class.isAssignableFrom(sourceType)) { // DataSource
boolean can = false;
for (Constructor cr : sourceType.getConstructors()) {
if (cr.getParameterCount() == 0) {
can = true;
break;
}
}
if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数
final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
@@ -266,8 +278,9 @@ public abstract class NodeServer {
}
}, DataSource.class);
//------------------------------------- 注册CacheSource --------------------------------------------------------
//------------------------------------- 注册 CacheSource --------------------------------------------------------
resourceFactory.register(new ResourceFactory.ResourceLoader() {
@Override
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
try {
if (field.getAnnotation(Resource.class) == null) return;
@@ -312,10 +325,46 @@ public abstract class NodeServer {
}
}
@Override
public boolean autoNone() {
return false;
}
}, CacheSource.class);
//------------------------------------- 注册 WebSocketNode --------------------------------------------------------
resourceFactory.register(new ResourceFactory.ResourceLoader() {
@Override
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
try {
if (field.getAnnotation(Resource.class) == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 WebSocketNode
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
if (nodeService == null) {
final HashSet<String> groups = new HashSet<>();
if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
(isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService);
}
resourceFactory.inject(nodeService, self);
field.set(src, nodeService);
if (Sncp.isRemote(nodeService)) {
remoteServices.add(nodeService);
} else {
if (field != null) rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
localServices.add(nodeService);
interceptorServices.add(nodeService);
if (consumer != null) consumer.accept(nodeService);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
}
}
@Override
public boolean autoNone() {
return false;
}
}, WebSocketNode.class);
}
@SuppressWarnings("unchecked")
@@ -335,7 +384,6 @@ public abstract class NodeServer {
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
//if (WebSocketNode.class.isAssignableFrom(serviceImplClass)) continue;
}
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
@@ -452,125 +500,6 @@ public abstract class NodeServer {
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
}
//尚未完整实现, 先屏蔽, 单个Service在多个Server中存在的情况下进行缓存的方案还未考虑清楚
@SuppressWarnings("unchecked")
private void loadPersistData() throws Exception {
File home = application.getHome();
if (home == null || !home.isDirectory()) return;
File cachedir = new File(home, "cache");
if (!cachedir.isDirectory()) return;
int port = this.server.getSocketAddress().getPort();
final String prefix = "persist-" + port + "-";
final BsonConvert convert = BsonFactory.create().skipAllIgnore(true).getConvert();
synchronized (this.application) {
for (final File file : cachedir.listFiles((dir, name) -> name.startsWith(prefix))) {
if (!file.getName().endsWith(".bat")) continue;
String classAndResname = file.getName().substring(prefix.length(), file.getName().length() - 4); //去掉尾部的.bat
int pos = classAndResname.indexOf('-');
String servtype = pos > 0 ? classAndResname.substring(0, pos) : classAndResname;
String resname = pos > 0 ? classAndResname.substring(pos + 1) : "";
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int b;
while ((b = in.read()) != '\n') out.write(b);
final String[] fieldNames = out.toString().split(",");
int timeout = (int) ((System.currentTimeMillis() - file.lastModified()) / 1000);
for (final Service service : this.localServices) {
if (!servtype.equals(Sncp.getResourceType(service).getName())) continue;
if (!resname.equals(Sncp.getResourceName(service))) continue;
for (final String fieldName : fieldNames) {
Field field = null;
Class clzz = service.getClass();
do {
try {
field = clzz.getDeclaredField(fieldName);
break;
} catch (Exception e) {
}
} while ((clzz = clzz.getSuperclass()) != Object.class);
field.setAccessible(true);
Object val = convert.convertFrom(field.getGenericType(), in);
Persist persist = field.getAnnotation(Persist.class);
if (persist.timeout() == 0 || persist.timeout() >= timeout) {
if (Modifier.isFinal(field.getModifiers())) {
if (Map.class.isAssignableFrom(field.getType())) {
((Map) field.get(service)).putAll((Map) val);
} else if (Collection.class.isAssignableFrom(field.getType())) {
((Collection) field.get(service)).addAll((Collection) val);
}
} else {
field.set(service, val);
}
}
if (in.read() != '\n') logger.log(Level.SEVERE, servtype + "'s [" + resname + "] load value error");
}
}
in.close();
}
}
}
//尚未完整实现, 先屏蔽
@SuppressWarnings("unchecked")
private void savePersistData() throws IOException {
File home = application.getHome();
if (home == null || !home.isDirectory()) return;
File cachedir = new File(home, "cache");
int port = this.server.getSocketAddress().getPort();
final String prefix = "persist-" + port + "-";
final BsonConvert convert = BsonFactory.create().skipAllIgnore(true).getConvert();
for (final Service service : this.localServices) {
Class clzz = service.getClass();
final Set<String> fieldNameSet = new HashSet<>();
final List<Field> fields = new ArrayList<>();
final StringBuilder sb = new StringBuilder();
do {
for (Field field : clzz.getDeclaredFields()) {
if (field.getAnnotation(Persist.class) == null) continue;
if (fieldNameSet.contains(field.getName())) continue;
if (Modifier.isStatic(field.getModifiers())) throw new RuntimeException(field + " cannot static on @" + Persist.class.getName() + " in " + clzz.getName());
if (Modifier.isFinal(field.getModifiers()) && !Map.class.isAssignableFrom(field.getType()) && !Collection.class.isAssignableFrom(field.getType())) {
throw new RuntimeException(field + " cannot final on @" + Persist.class.getName() + " in " + clzz.getName());
}
fieldNameSet.add(field.getName());
field.setAccessible(true);
try {
if (field.get(service) == null) continue;
} catch (Exception e) {
logger.log(Level.SEVERE, field + " get value error", e);
continue;
}
fields.add(field);
if (sb.length() > 0) sb.append(',');
sb.append(field.getName());
}
} while ((clzz = clzz.getSuperclass()) != Object.class);
if (fields.isEmpty()) continue; //没有数据需要缓存
// synchronized (this.application.localServices) {
// if (this.application.localServices.contains(service)) continue;
// this.application.localServices.add(service);
// }
if (!cachedir.isDirectory()) cachedir.mkdirs();
String resname = Sncp.getResourceName(service);
FileOutputStream out = new FileOutputStream(new File(cachedir, prefix + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname)) + ".bat"));
out.write(sb.toString().getBytes());
out.write('\n');
for (Field field : fields) {
Object val = null;
try {
val = field.get(service);
} catch (Exception e) {
logger.log(Level.SEVERE, field + " save value error", e);
}
convert.convertTo(out, field.getGenericType(), val);
out.write('\n');
}
out.close();
}
}
protected abstract ClassFilter<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -703,9 +632,12 @@ public abstract class NodeServer {
public void shutdown() throws IOException {
if (interceptor != null) interceptor.preShutdown(this);
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST);
localServices.forEach(y -> {
long s = System.currentTimeMillis();
if (finest) logger.finest(y + " is destroying");
y.destroy(Sncp.getConf(y));
if (finest) logger.finest(y + " was destroyed");
long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) {
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);

View File

@@ -44,7 +44,7 @@ public class NodeSncpServer extends NodeServer {
}
private static Server createServer(Application application, AnyValue serconf) {
return new SncpServer(application.getStartTime());
return new SncpServer(application.getStartTime(), application.getResourceFactory().createChild());
}
@Override
@@ -93,7 +93,7 @@ public class NodeSncpServer extends NodeServer {
for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
final SncpFilter filter = clazz.newInstance();
final SncpFilter filter = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.sncpServer.addSncpFilter(filter, filterConf);

View File

@@ -24,16 +24,19 @@ public class NodeWatchServer extends NodeHttpServer {
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Service> createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, WatchService.class, null, Annotation.class, "services", "service");
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Filter> createFilterClassFilter() {
return createClassFilter(null, null, WatchFilter.class, null, null, "filters", "filter");
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Servlet> createServletClassFilter() {
return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet");
}

View File

@@ -5,7 +5,13 @@
*/
package org.redkale.boot.watch;
import org.redkale.net.http.RestService;
import java.util.*;
import javax.annotation.Resource;
import org.redkale.boot.*;
import org.redkale.net.Server;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.Comment;
/**
*
@@ -14,4 +20,66 @@ import org.redkale.net.http.RestService;
@RestService(name = "server", catalog = "watch", repair = false)
public class ServerWatchService extends AbstractWatchService {
@Comment("不存在的Server节点")
public static final int RET_SERVER_NOT_EXISTS = 1602_0001;
@Resource
private Application application;
@RestMapping(name = "info", comment = "单个Server信息查询")
public RetResult info(@RestParam(name = "#port:") int port) {
NodeServer node = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getServer().getSocketAddress().getPort() == port) {
node = ns;
break;
}
}
if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + port + ") not found");
return new RetResult(formatToMap(node));
}
@RestMapping(name = "infos", comment = "Server信息查询")
public RetResult infos() {
Map<String, Object> rs = new LinkedHashMap<>();
for (NodeServer ns : application.getNodeServers()) {
Server server = ns.getServer();
rs.put("" + server.getSocketAddress().getPort(), formatToMap(ns));
}
return new RetResult(rs);
}
private Map<String, Object> formatToMap(NodeServer node) {
Server server = node.getServer();
Map<String, Object> rs = new LinkedHashMap<>();
String protocol = server.getProtocol();
if (node instanceof NodeSncpServer) {
protocol += "/SNCP";
} else if (node instanceof NodeWatchServer) {
protocol += "/WATCH";
} else if (node instanceof NodeHttpServer) {
protocol += "/HTTP";
} else {
NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class);
if (np != null && np.value().length > 0) protocol += "/" + np.value()[0];
}
rs.put("name", server.getName());
rs.put("protocol", protocol);
rs.put("address", server.getSocketAddress());
rs.put("threads", server.getThreads());
rs.put("backlog", server.getBacklog());
rs.put("bufferCapacity", server.getBufferCapacity());
rs.put("bufferPoolSize", server.getBufferPoolSize());
rs.put("charset", server.getCharset() == null ? "UTF-8" : server.getCharset().name());
rs.put("maxbody", server.getMaxbody());
rs.put("maxconns", server.getMaxconns());
rs.put("serverStartTime", server.getServerStartTime());
rs.put("responsePoolSize", server.getResponsePoolSize());
rs.put("readTimeoutSeconds", server.getReadTimeoutSeconds());
rs.put("writeTimeoutSeconds", server.getWriteTimeoutSeconds());
rs.put("createConnectionCount", server.getCreateConnectionCount());
rs.put("livingConnectionCount", server.getLivingConnectionCount());
rs.put("closedConnectionCount", server.getClosedConnectionCount());
return rs;
}
}

View File

@@ -0,0 +1,47 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 序列化时永久禁用该字段, 与ConvertColumn.ignore()的区别在于: ConvertDisabled不能通过ConvertEntity来解禁
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertDisabled {
/**
* 解析/序列化定制化的TYPE
*
* @return JSON or BSON or ALL
*/
ConvertType type() default ConvertType.ALL;
/**
* ConvertDisabled 的多用类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public static @interface ConvertDisableds {
ConvertDisabled[] value();
}
}

View File

@@ -53,6 +53,9 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
private final Set<Class> skipIgnores = new HashSet();
//key:需要屏蔽的字段value排除的字段名
private final ConcurrentHashMap<Class, Set<String>> ignoreAlls = new ConcurrentHashMap();
private boolean skipAllIgnore = false;
protected ConvertFactory(ConvertFactory<R, W> parent, boolean tiny) {
@@ -119,7 +122,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class));
this.register(HttpCookie.class, new Creator<HttpCookie>() {
@Override
@Creator.ConstructorParameters({"name", "value"})
@ConstructorParameters({"name", "value"})
public HttpCookie create(Object... params) {
return new HttpCookie((String) params[0], (String) params[1]);
}
@@ -149,15 +152,41 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return this;
}
public ConvertColumnEntry findRef(AccessibleObject element) {
if (element == null) return null;
ConvertColumnEntry en = this.columnEntrys.get(element);
if (en != null) return en;
final ConvertType ct = this.getConvertType();
ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class);
public boolean isConvertDisabled(AccessibleObject element) {
ConvertDisabled[] ccs = element.getAnnotationsByType(ConvertDisabled.class);
if (ccs.length == 0 && element instanceof Method) {
final Method method = (Method) element;
String fieldName = readGetSetFieldName(method);
if (fieldName != null) {
try {
ccs = method.getDeclaringClass().getDeclaredField(fieldName).getAnnotationsByType(ConvertDisabled.class);
} catch (Exception e) { //说明没有该字段,忽略异常
}
}
}
final ConvertType ct = this.getConvertType();
for (ConvertDisabled ref : ccs) {
if (ref.type().contains(ct)) return true;
}
return false;
}
public ConvertColumnEntry findRef(AccessibleObject element) {
if (element == null) return null;
ConvertColumnEntry en = this.columnEntrys.get(element);
Set<String> onlyColumns = null;
if (element instanceof Method) {
onlyColumns = ignoreAlls.get(((Method) element).getDeclaringClass());
} else if (element instanceof Field) {
onlyColumns = ignoreAlls.get(((Field) element).getDeclaringClass());
}
if (en != null && onlyColumns == null) return en;
final ConvertType ct = this.getConvertType();
ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class);
String fieldName = null;
if (ccs.length == 0 && element instanceof Method) {
final Method method = (Method) element;
fieldName = readGetSetFieldName(method);
if (fieldName != null) {
try {
ccs = method.getDeclaringClass().getDeclaredField(fieldName).getAnnotationsByType(ConvertColumn.class);
@@ -165,8 +194,22 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
}
}
}
if (onlyColumns != null && fieldName == null) {
if (element instanceof Method) {
fieldName = readGetSetFieldName((Method) element);
} else if (element instanceof Field) {
fieldName = ((Field) element).getName();
}
}
if (ccs.length == 0 && onlyColumns != null && fieldName != null) {
if (!onlyColumns.contains(fieldName)) return new ConvertColumnEntry(fieldName, true);
}
for (ConvertColumn ref : ccs) {
if (ref.type().contains(ct)) {
if (onlyColumns != null && fieldName != null) {
String realName = ref.name().isEmpty() ? fieldName : ref.name();
if (!onlyColumns.contains(realName)) return new ConvertColumnEntry(realName, true);
}
ConvertColumnEntry entry = new ConvertColumnEntry(ref);
if (skipAllIgnore) {
entry.setIgnore(false);
@@ -299,6 +342,22 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
skipIgnores.add(type);
}
/**
* 屏蔽指定类所有字段,仅仅保留指定字段 <br>
* <b>注意: 该配置优先级高于skipAllIgnore和ConvertColumnEntry配置</b>
*
* @param type 指定的类
* @param excludeColumns 需要排除的字段名
*/
public final void registerIgnoreAll(final Class type, String... excludeColumns) {
Set<String> set = ignoreAlls.get(type);
if (set == null) {
ignoreAlls.put(type, new HashSet<>(Arrays.asList(excludeColumns)));
} else {
set.addAll(Arrays.asList(excludeColumns));
}
}
public final void register(final Class type, boolean ignore, String... columns) {
for (String column : columns) {
register(type, column, new ConvertColumnEntry(column, ignore));

View File

@@ -17,6 +17,7 @@ public enum ConvertType {
JSON(1),
BSON(2),
DIY(64),
ALL(127);
private final int value;

View File

@@ -31,7 +31,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
protected Creator<T> creator;
protected DeMember<R, T, ?>[] creatorConstructorMembers;
protected DeMember<R, T, ?>[] creatorConstructorMembers = new DeMember[0];
protected DeMember<R, T, ?>[] members;
@@ -46,6 +46,14 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.typeClass = (Class) pt.getRawType();
} else if (type instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) type;
Type[] ts = tv.getBounds();
if (ts.length == 1 && ts[0] instanceof Class) {
this.typeClass = (Class) ts[0];
} else {
throw new ConvertException("[" + type + "] is no a class or ParameterizedType");
}
} else {
this.typeClass = (Class) type;
}
@@ -61,23 +69,33 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
if (type instanceof ParameterizedType) {
final ParameterizedType pts = (ParameterizedType) type;
clazz = (Class) (pts).getRawType();
} else if (type instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) type;
Type[] ts = tv.getBounds();
if (ts.length == 1 && ts[0] instanceof Class) {
clazz = (Class) ts[0];
} else {
throw new ConvertException("[" + type + "] is no a class or TypeVariable");
}
} else if (!(type instanceof Class)) {
throw new ConvertException("[" + type + "] is no a class");
} else {
clazz = (Class) type;
}
this.creator = factory.loadCreator(clazz);
if (this.creator == null) throw new ConvertException("Cannot create a creator for " + clazz);
if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
this.creator = factory.loadCreator(clazz);
if (this.creator == null) throw new ConvertException("Cannot create a creator for " + clazz);
}
final Set<DeMember> list = new HashSet();
final String[] cps = ObjectEncoder.findConstructorProperties(this.creator);
try {
ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type);
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
@@ -89,7 +107,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
if (method.isSynthetic()) continue;
if (method.getName().length() < 4) continue;
if (!method.getName().startsWith("set")) continue;
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 1) continue;
if (method.getReturnType() != void.class) continue;
if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
@@ -102,7 +120,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
}
ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(method.getGenericParameterTypes()[0], this.type);
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
@@ -132,7 +150,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
} catch (NoSuchMethodException ex) {
getter = clazz.getMethod("is" + mn);
}
Type t = TypeToken.createClassType(getter.getGenericParameterTypes()[0], this.type);
Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, getter, null), factory.loadDecoder(t)));
}
}
@@ -185,6 +203,11 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
}
}
}
if (this.creator == null) {
if (typeClass.isInterface() || Modifier.isAbstract(typeClass.getModifiers())) {
throw new ConvertException("[" + typeClass + "] is a interface or abstract class, cannot create it's Creator.");
}
}
if (this.creatorConstructorMembers == null) { //空构造函数
final T result = this.creator.create();
while (in.hasNext()) {

View File

@@ -41,6 +41,14 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.typeClass = (Class) pt.getRawType();
} else if (type instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) type;
Type[] ts = tv.getBounds();
if (ts.length == 1 && ts[0] instanceof Class) {
this.typeClass = (Class) ts[0];
} else {
throw new ConvertException("[" + type + "] is no a class or ParameterizedType");
}
} else {
this.typeClass = (Class) type;
}
@@ -66,9 +74,10 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type);
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
@@ -80,7 +89,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
if (method.getName().length() < 3) continue;
if (method.getName().equals("getClass")) continue;
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue;
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
@@ -93,7 +102,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
}
ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(method.getGenericReturnType(), this.type);
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
@@ -206,8 +215,9 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
}
static String[] findConstructorProperties(Creator creator) {
if (creator == null) return null;
try {
Creator.ConstructorParameters cps = creator.getClass().getMethod("create", Object[].class).getAnnotation(Creator.ConstructorParameters.class);
ConstructorParameters cps = creator.getClass().getMethod("create", Object[].class).getAnnotation(ConstructorParameters.class);
return cps == null ? null : cps.value();
} catch (Exception e) {
return null;

View File

@@ -34,6 +34,7 @@ public class OptionalCoder<R extends Reader, W extends Writer, T> extends Simple
private final Object lock = new Object();
@SuppressWarnings("unchecked")
public OptionalCoder(final ConvertFactory factory, final Type type) {
this.type = type;
try {

View File

@@ -25,6 +25,7 @@ public final class BigIntegerSimpledCoder<R extends Reader, W extends Writer> ex
public static final BigIntegerSimpledCoder instance = new BigIntegerSimpledCoder();
@Override
@SuppressWarnings("unchecked")
public void convertTo(W out, BigInteger value) {
if (value == null) {
out.writeNull();
@@ -34,6 +35,7 @@ public final class BigIntegerSimpledCoder<R extends Reader, W extends Writer> ex
}
@Override
@SuppressWarnings("unchecked")
public BigInteger convertFrom(R in) {
byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in);
return bytes == null ? null : new BigInteger(bytes);

View File

@@ -27,6 +27,7 @@ public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends
public static final DLongSimpledCoder instance = new DLongSimpledCoder();
@Override
@SuppressWarnings("unchecked")
public void convertTo(final W out, final DLong value) {
if (value == null) {
out.writeNull();
@@ -36,6 +37,7 @@ public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends
}
@Override
@SuppressWarnings("unchecked")
public DLong convertFrom(R in) {
byte[] bs = bsSimpledCoder.convertFrom(in);
if (bs == null) return null;

View File

@@ -74,6 +74,7 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
public static final DoubleStreamSimpledCoder instance = new DoubleStreamSimpledCoder();
@Override
@SuppressWarnings("unchecked")
public void convertTo(W out, DoubleStream values) {
if (values == null) {
out.writeNull();
@@ -83,6 +84,7 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
}
@Override
@SuppressWarnings("unchecked")
public DoubleStream convertFrom(R in) {
double[] value = DoubleArraySimpledCoder.instance.convertFrom(in);
return value == null ? null : DoubleStream.of(value);

View File

@@ -74,6 +74,7 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
public static final IntStreamSimpledCoder instance = new IntStreamSimpledCoder();
@Override
@SuppressWarnings("unchecked")
public void convertTo(W out, IntStream values) {
if (values == null) {
out.writeNull();
@@ -83,6 +84,7 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
}
@Override
@SuppressWarnings("unchecked")
public IntStream convertFrom(R in) {
int[] value = IntArraySimpledCoder.instance.convertFrom(in);
return value == null ? null : IntStream.of(value);

View File

@@ -74,6 +74,7 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
public static final LongStreamSimpledCoder instance = new LongStreamSimpledCoder();
@Override
@SuppressWarnings("unchecked")
public void convertTo(W out, LongStream values) {
if (values == null) {
out.writeNull();
@@ -83,6 +84,7 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
}
@Override
@SuppressWarnings("unchecked")
public LongStream convertFrom(R in) {
long[] value = LongArraySimpledCoder.instance.convertFrom(in);
return value == null ? null : LongStream.of(value);

View File

@@ -22,8 +22,6 @@ import org.redkale.util.*;
*/
public class JsonByteBufferWriter extends JsonWriter {
protected static final Charset UTF8 = Charset.forName("UTF-8");
protected Charset charset;
private final Supplier<ByteBuffer> supplier;
@@ -38,7 +36,7 @@ public class JsonByteBufferWriter extends JsonWriter {
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) {
this.tiny = tiny;
this.charset = UTF8.equals(charset) ? null : charset;
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
this.supplier = supplier;
}
@@ -155,7 +153,7 @@ public class JsonByteBufferWriter extends JsonWriter {
if (charset == null) { //UTF-8
final int limit = start + len;
for (int i = start; i < limit; i++) {
buffer = putChar(buffer, chs[i]);
buffer = putUTF8Char(buffer, chs[i]);
}
} else {
while (bb.hasRemaining()) {
@@ -169,7 +167,7 @@ public class JsonByteBufferWriter extends JsonWriter {
}
}
private ByteBuffer putChar(ByteBuffer buffer, char c) {
private ByteBuffer putUTF8Char(ByteBuffer buffer, char c) {
if (c < 0x80) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) c);

View File

@@ -12,6 +12,8 @@ import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import javax.net.ssl.SSLContext;
/**
*
@@ -22,6 +24,8 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCloseable {
protected SSLContext sslContext;
protected Map<String, Object> attributes; //用于存储绑定在Connection上的对象集合
protected Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes
@@ -30,11 +34,13 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
protected volatile long writetime;
//关闭数
AtomicLong closedCounter;
//在线数
AtomicLong livingCounter;
protected AtomicLong livingCounter;
//关闭数
protected AtomicLong closedCounter;
protected Consumer<AsyncConnection> beforeCloseListener;
public final long getLastReadTime() {
return readtime;
@@ -46,17 +52,39 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public abstract boolean isTCP();
public abstract boolean shutdownInput();
public abstract boolean shutdownOutput();
public abstract <T> boolean setOption(SocketOption<T> name, T value);
public abstract Set<SocketOption<?>> supportedOptions();
public abstract SocketAddress getRemoteAddress();
public abstract SocketAddress getLocalAddress();
public abstract int getReadTimeoutSecond();
public abstract int getReadTimeoutSeconds();
public abstract int getWriteTimeoutSecond();
public abstract int getWriteTimeoutSeconds();
public abstract void setReadTimeoutSecond(int readTimeoutSecond);
public abstract void setReadTimeoutSeconds(int readTimeoutSeconds);
public abstract void setWriteTimeoutSecond(int writeTimeoutSecond);
public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds);
@Override
public abstract Future<Integer> read(ByteBuffer dst);
@Override
public abstract <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler);
public abstract <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler);
@Override
public abstract Future<Integer> write(ByteBuffer src);
@Override
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(srcs, 0, srcs.length, attachment, handler);
@@ -71,6 +99,11 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
}
}
public AsyncConnection beforeCloseListener(Consumer<AsyncConnection> beforeCloseListener) {
this.beforeCloseListener = beforeCloseListener;
return this;
}
@Override
public void close() throws IOException {
if (closedCounter != null) {
@@ -81,6 +114,11 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
livingCounter.decrementAndGet();
livingCounter = null;
}
if (beforeCloseListener != null)
try {
beforeCloseListener.accept(this);
} catch (Exception io) {
}
if (attributes == null) return;
try {
for (Object obj : attributes.values()) {
@@ -125,44 +163,44 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
/**
* 创建TCP协议客户端连接
*
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSecond 读取超时秒数
* @param writeTimeoutSecond 写入超时秒数
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
final int readTimeoutSecond, final int writeTimeoutSecond) {
return createTCP(group, address, false, readTimeoutSecond, writeTimeoutSecond);
final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param noDelay TcpNoDelay
* @param readTimeoutSecond 读取超时秒数
* @param writeTimeoutSecond 写入超时秒数
* @param address 连接点子
* @param sslContext SSLContext
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
final boolean noDelay, final int readTimeoutSecond, final int writeTimeoutSecond) {
final CompletableFuture future = new CompletableFuture();
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SSLContext sslContext,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
try {
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (IOException e) {
}
channel.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
if (noDelay) {
try {
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
} catch (IOException e) {
}
}
future.complete(create(channel, address, readTimeoutSecond, writeTimeoutSecond));
future.complete(create(channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds));
}
@Override
@@ -176,287 +214,6 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
return future;
}
private static class BIOUDPAsyncConnection extends AsyncConnection {
private int readTimeoutSecond;
private int writeTimeoutSecond;
private final DatagramChannel channel;
private final SocketAddress remoteAddress;
private final boolean client;
public BIOUDPAsyncConnection(final DatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
this.channel = ch;
this.client = client0;
this.readTimeoutSecond = readTimeoutSecond0;
this.writeTimeoutSecond = writeTimeoutSecond0;
this.remoteAddress = addr;
}
@Override
public void setReadTimeoutSecond(int readTimeoutSecond) {
this.readTimeoutSecond = readTimeoutSecond;
}
@Override
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
this.writeTimeoutSecond = writeTimeoutSecond;
}
@Override
public int getReadTimeoutSecond() {
return this.readTimeoutSecond;
}
@Override
public int getWriteTimeoutSecond() {
return this.writeTimeoutSecond;
}
@Override
public final SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = 0;
for (int i = offset; i < offset + length; i++) {
rs += channel.send(srcs[i], remoteAddress);
if (i != offset) Thread.sleep(10);
}
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (Exception e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public Future<Integer> read(ByteBuffer dst) {
try {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public Future<Integer> write(ByteBuffer src) {
try {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public final void close() throws IOException {
super.close();
if (client) channel.close();
}
@Override
public final boolean isOpen() {
return channel.isOpen();
}
@Override
public final boolean isTCP() {
return false;
}
}
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
return new BIOUDPAsyncConnection(ch, addr, client0, readTimeoutSecond0, writeTimeoutSecond0);
}
private static class BIOTCPAsyncConnection extends AsyncConnection {
private int readTimeoutSecond;
private int writeTimeoutSecond;
private final Socket socket;
private final ReadableByteChannel readChannel;
private final WritableByteChannel writeChannel;
private final SocketAddress remoteAddress;
public BIOTCPAsyncConnection(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
this.socket = socket;
ReadableByteChannel rc = null;
WritableByteChannel wc = null;
try {
socket.setSoTimeout(Math.max(readTimeoutSecond0, writeTimeoutSecond0));
rc = Channels.newChannel(socket.getInputStream());
wc = Channels.newChannel(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
this.readChannel = rc;
this.writeChannel = wc;
this.readTimeoutSecond = readTimeoutSecond0;
this.writeTimeoutSecond = writeTimeoutSecond0;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = socket.getRemoteSocketAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isTCP() {
return true;
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
return socket.getLocalSocketAddress();
}
@Override
public int getReadTimeoutSecond() {
return readTimeoutSecond;
}
@Override
public int getWriteTimeoutSecond() {
return writeTimeoutSecond;
}
@Override
public void setReadTimeoutSecond(int readTimeoutSecond) {
this.readTimeoutSecond = readTimeoutSecond;
}
@Override
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
this.writeTimeoutSecond = writeTimeoutSecond;
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = 0;
for (int i = offset; i < offset + length; i++) {
rs += writeChannel.write(srcs[i]);
}
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = readChannel.read(dst);
this.readtime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public Future<Integer> read(ByteBuffer dst) {
try {
int rs = readChannel.read(dst);
this.readtime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = writeChannel.write(src);
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public Future<Integer> write(ByteBuffer src) {
try {
int rs = writeChannel.write(src);
this.writetime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() throws IOException {
super.close();
this.socket.close();
}
@Override
public boolean isOpen() {
return !socket.isClosed();
}
}
/**
* 通常用于 ssl socket
*
@@ -469,141 +226,68 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
}
public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
return new BIOTCPAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0);
return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
}
private static class AIOTCPAsyncConnection extends AsyncConnection {
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 AIOTCPAsyncConnection(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
this.channel = ch;
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) {
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final AsynchronousSocketChannel ch) {
return create(ch, null, 0, 0);
}
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond, final int writeTimeoutSecond) {
return new AIOTCPAsyncConnection(ch, addr0, readTimeoutSecond, writeTimeoutSecond);
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final Context context) {
return new 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

@@ -13,6 +13,7 @@ import java.nio.charset.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.net.ssl.SSLContext;
import org.redkale.convert.bson.*;
import org.redkale.convert.json.*;
import org.redkale.util.*;
@@ -27,14 +28,15 @@ import org.redkale.util.*;
*/
public class Context {
private static final Charset UTF8 = Charset.forName("UTF-8");
//服务启动时间
protected final long serverStartTime;
//Server的线程池
protected final ThreadPoolExecutor executor;
//SSL
protected final SSLContext sslContext;
//ByteBuffer的容量默认8K
protected final int bufferCapacity;
@@ -53,14 +55,20 @@ public class Context {
//字符集
protected final Charset charset;
//最大连接数, 为0表示没限制
protected final int maxconns;
//请求内容的大小上限, 默认64K
protected final int maxbody;
//keep alive IO读取的超时时间
protected final int aliveTimeoutSeconds;
//IO读取的超时时间
protected final int readTimeoutSecond;
protected final int readTimeoutSeconds;
//IO写入的超时时间
protected final int writeTimeoutSecond;
protected final int writeTimeoutSeconds;
//日志Logger
protected final Logger logger;
@@ -71,24 +79,52 @@ public class Context {
//JSON操作工厂
protected final JsonFactory jsonFactory;
public Context(long serverStartTime, Logger logger, ThreadPoolExecutor executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final int readTimeoutSecond, final int writeTimeoutSecond) {
//依赖注入工厂类
protected final ResourceFactory resourceFactory;
public Context(ContextConfig config) {
this(config.serverStartTime, config.logger, config.executor, config.sslContext,
config.bufferCapacity, config.bufferPool, config.responsePool, config.maxconns, config.maxbody,
config.charset, config.address, config.resourceFactory, config.prepare,
config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds);
}
public Context(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext,
int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, final int maxconns,
final int maxbody, Charset charset, InetSocketAddress address, ResourceFactory resourceFactory,
final PrepareServlet prepare, final int aliveTimeoutSeconds, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
this.serverStartTime = serverStartTime;
this.logger = logger;
this.executor = executor;
this.sslContext = sslContext;
this.bufferCapacity = bufferCapacity;
this.bufferPool = bufferPool;
this.responsePool = responsePool;
this.maxconns = maxconns;
this.maxbody = maxbody;
this.charset = UTF8.equals(charset) ? null : charset;
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
this.address = address;
this.prepare = prepare;
this.readTimeoutSecond = readTimeoutSecond;
this.writeTimeoutSecond = writeTimeoutSecond;
this.resourceFactory = resourceFactory;
this.aliveTimeoutSeconds = aliveTimeoutSeconds;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.jsonFactory = JsonFactory.root();
this.bsonFactory = BsonFactory.root();
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
public SSLContext getSSLContext() {
return sslContext;
}
public int getMaxconns() {
return maxconns;
}
public int getMaxbody() {
return maxbody;
}
@@ -125,15 +161,19 @@ public class Context {
return bufferPool;
}
protected Consumer<ByteBuffer> getBufferConsumer() {
return bufferPool;
}
public ByteBuffer pollBuffer() {
return bufferPool.get();
}
public void offerBuffer(ByteBuffer buffer) {
protected void offerBuffer(ByteBuffer buffer) {
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
protected void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return;
for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer);
@@ -144,12 +184,16 @@ public class Context {
return logger;
}
public int getReadTimeoutSecond() {
return readTimeoutSecond;
public int getAliveTimeoutSeconds() {
return aliveTimeoutSeconds;
}
public int getWriteTimeoutSecond() {
return writeTimeoutSecond;
public int getReadTimeoutSeconds() {
return readTimeoutSeconds;
}
public int getWriteTimeoutSeconds() {
return writeTimeoutSeconds;
}
public JsonConvert getJsonConvert() {
@@ -159,4 +203,60 @@ public class Context {
public BsonConvert getBsonConvert() {
return bsonFactory.getConvert();
}
public static class ContextConfig {
//服务启动时间
public long serverStartTime;
//Server的线程池
public ThreadPoolExecutor executor;
//SSL
public SSLContext sslContext;
//ByteBuffer的容量默认8K
public int bufferCapacity;
//ByteBuffer对象池
public ObjectPool<ByteBuffer> bufferPool;
//Response对象池
public ObjectPool<Response> responsePool;
//服务的根Servlet
public PrepareServlet prepare;
//服务的监听地址
public InetSocketAddress address;
//字符集
public Charset charset;
//请求内容的大小上限, 默认64K
public int maxbody;
//最大连接数, 为0表示没限制
public int maxconns;
//keep alive IO读取的超时时间
public int aliveTimeoutSeconds;
//IO读取的超时时间
public int readTimeoutSeconds;
//IO写入的超时时间
public int writeTimeoutSeconds;
//日志Logger
public Logger logger;
//依赖注入工厂类
public ResourceFactory resourceFactory;
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.util.function.*;
/**
* 加密解密接口
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface Cryptor {
/**
* 加密
*
* @param buffers 待加密数据
* @param supplier ByteBuffer生成器
* @param consumer ByteBuffer回收器
*
* @return 加密后数据
*/
public ByteBuffer[] encrypt(ByteBuffer[] buffers, final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer);
/**
* 解密
*
* @param buffers 待解密数据
* @param supplier ByteBuffer生成器
* @param consumer ByteBuffer回收器
*
* @return 解密后数据
*/
public ByteBuffer[] decrypt(ByteBuffer[] buffers, final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer);
}

View File

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

View File

@@ -214,11 +214,11 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
executeCounter.incrementAndGet();
final int rs = request.readHeader(buffer);
if (rs < 0) {
response.context.offerBuffer(buffer);
request.offerReadBuffer(buffer);
if (rs != Integer.MIN_VALUE) illRequestCounter.incrementAndGet();
response.finish(true);
} else if (rs == 0) {
response.context.offerBuffer(buffer);
request.offerReadBuffer(buffer);
request.prepare();
response.filter = this.headFilter;
response.servlet = this;
@@ -236,7 +236,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
buffer.clear();
request.channel.read(buffer, buffer, this);
} else {
response.context.offerBuffer(buffer);
request.offerReadBuffer(buffer);
request.prepare();
try {
response.filter = PrepareServlet.this.headFilter;
@@ -253,7 +253,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
illRequestCounter.incrementAndGet();
response.context.offerBuffer(buffer);
request.offerReadBuffer(buffer);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, forece to close channel ", exc);
}

View File

@@ -7,11 +7,9 @@ package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.util.AnyValue;
/**
* 协议底层Server
@@ -32,10 +30,12 @@ public abstract class ProtocolServer {
//在线数
protected final AtomicLong livingCounter = new AtomicLong();
protected final Context context;
//最大连接数小于1表示无限制
protected int maxconns;
public abstract void open() throws IOException;
public abstract void open(AnyValue config) throws IOException;
public abstract void bind(SocketAddress local, int backlog) throws IOException;
@@ -43,15 +43,14 @@ public abstract class ProtocolServer {
public abstract <T> void setOption(SocketOption<T> name, T value) throws IOException;
public abstract void accept();
public void setMaxconns(int maxconns) {
this.maxconns = maxconns;
}
public abstract void accept() throws IOException;
public abstract void close() throws IOException;
public abstract AsynchronousChannelGroup getChannelGroup();
protected ProtocolServer(Context context) {
this.context = context;
this.maxconns = context.getMaxconns();
}
public long getCreateCount() {
return createCounter.longValue();
@@ -66,176 +65,31 @@ public abstract class ProtocolServer {
}
//---------------------------------------------------------------------
public static ProtocolServer create(String protocol, Context context) {
if ("TCP".equalsIgnoreCase(protocol)) return new ProtocolTCPServer(context);
if ("UDP".equalsIgnoreCase(protocol)) return new ProtocolUDPServer(context);
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
}
private static final class ProtocolUDPServer extends ProtocolServer {
private boolean running;
private final Context context;
private DatagramChannel serverChannel;
public ProtocolUDPServer(Context context) {
this.context = context;
}
@Override
public void open() throws IOException {
DatagramChannel ch = DatagramChannel.open();
ch.configureBlocking(true);
this.serverChannel = ch;
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public void accept() {
final DatagramChannel serchannel = this.serverChannel;
final int readTimeoutSecond = this.context.readTimeoutSecond;
final int writeTimeoutSecond = this.context.writeTimeoutSecond;
final CountDownLatch cdl = new CountDownLatch(1);
this.running = true;
new Thread() {
@Override
public void run() {
cdl.countDown();
while (running) {
final ByteBuffer buffer = context.pollBuffer();
try {
SocketAddress address = serchannel.receive(buffer);
buffer.flip();
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond);
context.runAsync(new PrepareRunner(context, conn, buffer));
} catch (Exception e) {
context.offerBuffer(buffer);
}
}
}
}.start();
try {
cdl.await();
} catch (Exception e) {
e.printStackTrace();
public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) {
if (netimpl != null) netimpl = netimpl.trim();
if ("TCP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {
return new TcpAioProtocolServer(context);
} else if ("aio".equalsIgnoreCase(netimpl)) {
return new TcpAioProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
return new TcpNioProtocolServer(context);
}
} else if ("UDP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {
return new UdpBioProtocolServer(context);
} else if ("bio".equalsIgnoreCase(netimpl)) {
return new UdpBioProtocolServer(context);
}
} else if (netimpl == null || netimpl.isEmpty()) {
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
}
@Override
public void close() throws IOException {
this.running = false;
this.serverChannel.close();
}
@Override
public AsynchronousChannelGroup getChannelGroup() {
return null;
}
@Override
public long getCreateCount() {
return -1;
}
@Override
public long getClosedCount() {
return -1;
}
@Override
public long getLivingCount() {
return -1;
}
}
private static final class ProtocolTCPServer extends ProtocolServer {
private final Context context;
private AsynchronousChannelGroup group;
private AsynchronousServerSocketChannel serverChannel;
public ProtocolTCPServer(Context context) {
this.context = context;
}
@Override
public void open() throws IOException {
group = AsynchronousChannelGroup.withCachedThreadPool(context.executor, 1);
this.serverChannel = AsynchronousServerSocketChannel.open(group);
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local, backlog);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public void accept() {
final AsynchronousServerSocketChannel serchannel = this.serverChannel;
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
serchannel.accept(null, this);
if (maxconns > 0 && livingCounter.get() >= maxconns) {
try {
channel.close();
} catch (Exception e) {
}
return;
}
createCounter.incrementAndGet();
livingCounter.incrementAndGet();
AsyncConnection conn = AsyncConnection.create(channel, null, context.readTimeoutSecond, context.writeTimeoutSecond);
conn.livingCounter = livingCounter;
conn.closedCounter = closedCounter;
context.submitAsync(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;
try {
if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
Class clazz = classLoader.loadClass(netimpl);
return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context);
} catch (Exception e) {
throw new RuntimeException("ProtocolServer(netimple=" + netimpl + ") newinstance error", e);
}
}

View File

@@ -33,6 +33,8 @@ public abstract class Request<C extends Context> {
protected AsyncConnection channel;
protected ByteBuffer readBuffer;
/**
* properties 与 attributes 的区别在于调用recycle时 attributes会被清空而properties会保留;
* properties 通常存放需要永久绑定在request里的一些对象
@@ -43,10 +45,28 @@ public abstract class Request<C extends Context> {
protected Request(C context) {
this.context = context;
this.readBuffer = context.pollBuffer();
this.bsonConvert = context.getBsonConvert();
this.jsonConvert = context.getJsonConvert();
}
protected ByteBuffer pollReadBuffer() {
ByteBuffer buffer = this.readBuffer;
this.readBuffer = null;
if (buffer == null) buffer = context.pollBuffer();
return buffer;
}
protected void offerReadBuffer(ByteBuffer buffer) {
if (buffer == null) return;
if (this.readBuffer == null) {
buffer.clear();
this.readBuffer = buffer;
} else {
context.offerBuffer(buffer);
}
}
/**
* 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。
*
@@ -71,7 +91,7 @@ public abstract class Request<C extends Context> {
createtime = 0;
keepAlive = false;
attributes.clear();
channel = null; // close it by response
channel = null; // close it by response
}
protected <T> T setProperty(String name, T value) {

View File

@@ -8,7 +8,7 @@ package org.redkale.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.function.BiConsumer;
import java.util.function.*;
import java.util.logging.Level;
/**
@@ -30,6 +30,10 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected AsyncConnection channel;
protected ByteBuffer writeHeadBuffer;
protected ByteBuffer writeBodyBuffer;
private boolean inited = true;
protected Object output; //输出的结果对象
@@ -40,6 +44,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected Servlet<C, R, ? extends Response<C, R>> servlet;
private Supplier<ByteBuffer> bodyBufferSupplier;
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
@Override
@@ -47,17 +53,31 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
context.offerBuffer(attachment);
offerResponseBuffer(attachment);
finish();
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
context.offerBuffer(attachment);
offerResponseBuffer(attachment);
finish(true);
}
private void offerResponseBuffer(ByteBuffer attachment) {
if (writeHeadBuffer == null) {
if (context.bufferPool.getRecyclerPredicate().test(attachment)) {
writeHeadBuffer = attachment;
}
} else if (writeBodyBuffer == null) {
if (context.bufferPool.getRecyclerPredicate().test(attachment)) {
writeBodyBuffer = attachment;
}
} else {
context.offerBuffer(attachment);
}
}
};
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
@@ -74,26 +94,70 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (index >= 0) {
channel.write(attachments, index, attachments.length - index, attachments, this);
} else {
for (ByteBuffer attachment : attachments) {
context.offerBuffer(attachment);
}
offerResponseBuffer(attachments);
finish();
}
}
@Override
public void failed(Throwable exc, final ByteBuffer[] attachments) {
for (ByteBuffer attachment : attachments) {
context.offerBuffer(attachment);
}
offerResponseBuffer(attachments);
finish(true);
}
private void offerResponseBuffer(ByteBuffer[] attachments) {
int start = 0;
if (writeHeadBuffer == null && attachments.length > start) {
if (context.bufferPool.getRecyclerPredicate().test(attachments[start])) {
writeHeadBuffer = attachments[start];
start++;
}
}
if (writeBodyBuffer == null && attachments.length > start) {
if (context.bufferPool.getRecyclerPredicate().test(attachments[start])) {
writeBodyBuffer = attachments[start];
start++;
}
}
for (int i = start; i < attachments.length; i++) {
context.offerBuffer(attachments[i]);
}
}
};
protected Response(C context, final R request) {
this.context = context;
this.request = request;
this.writeHeadBuffer = context.pollBuffer();
this.writeBodyBuffer = context.pollBuffer();
this.bodyBufferSupplier = () -> {
ByteBuffer buffer = writeBodyBuffer;
if (buffer == null) return context.pollBuffer();
writeBodyBuffer = null;
return buffer;
};
}
protected ByteBuffer pollWriteReadBuffer() {
ByteBuffer buffer = this.writeHeadBuffer;
this.writeHeadBuffer = null;
if (buffer == null) buffer = context.pollBuffer();
return buffer;
}
protected ByteBuffer pollWriteBodyBuffer() {
ByteBuffer buffer = this.writeBodyBuffer;
this.writeBodyBuffer = null;
if (buffer == null) buffer = context.pollBuffer();
return buffer;
}
protected Supplier<ByteBuffer> getBodyBufferSupplier() {
return bodyBufferSupplier;
}
protected void offerBuffer(ByteBuffer... buffers) {
context.offerBuffer(buffers);
}
protected AsyncConnection removeChannel() {
@@ -109,28 +173,12 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected boolean recycle() {
if (!inited) return false;
boolean keepAlive = request.keepAlive;
if (recycleListener != null) {
try {
recycleListener.accept(request, this);
} catch (Exception e) {
context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e);
}
recycleListener = null;
}
this.output = null;
this.filter = null;
this.servlet = null;
request.recycle();
if (channel != null) {
if (keepAlive) {
this.context.runAsync(new PrepareRunner(context, channel, null));
} else {
try {
if (channel.isOpen()) channel.close();
} catch (Exception e) {
}
}
channel.dispose();
channel = null;
}
this.inited = false;
@@ -195,7 +243,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (!this.inited) return; //避免重复关闭
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
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) {

View File

@@ -0,0 +1,20 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public enum SSLClientAuth {
NONE,
NEED,
WANT,
CLIENT;
}

View File

@@ -0,0 +1,65 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import javax.net.ssl.*;
import org.redkale.util.*;
/**
* 根据配置生成SSLContext
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface SSLCreator {
default SSLContext create(Server server, AnyValue sslConf) throws Exception {
String keyfile = sslConf.getValue("keystorefile");
String keypass = sslConf.getValue("keystorepass", "");
KeyManager[] keyManagers = null;
if (keyfile != null) {
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyfile), keypass.toCharArray());
kmf.init(ks, keypass.toCharArray());
keyManagers = kmf.getKeyManagers();
}
String trustfile = sslConf.getValue("truststorefile");
String trustpass = sslConf.getValue("truststorepass", "");
TrustManager[] trustManagers;
if (trustfile != null) {
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new FileInputStream(trustfile), trustpass.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
trustManagers = tmf.getTrustManagers();
} else {
trustManagers = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, new SecureRandom());
return sslContext;
}
}

View File

@@ -13,6 +13,7 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.redkale.util.*;
/**
@@ -31,6 +32,10 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT";
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());
//-------------------------------------------------------------
@@ -43,9 +48,18 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//应用层协议名
protected final String protocol;
//依赖注入工厂类
protected final ResourceFactory resourceFactory;
//服务的根Servlet
protected final PrepareServlet<K, C, R, P, S> prepare;
//ClassLoader
protected RedkaleClassLoader serverClassLoader;
//SSL
protected SSLContext sslContext;
//服务的上下文对象
protected C context;
@@ -79,21 +93,25 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//Response池大小
protected int responsePoolSize;
//最大连接数, 为0表示没限制
protected int maxconns;
//请求包大小的上限,单位:字节
protected int maxbody;
//Keep-Alive IO读取的超时秒数小于1视为不设置
protected int aliveTimeoutSeconds;
//IO读取的超时秒数小于1视为不设置
protected int readTimeoutSecond;
protected int readTimeoutSeconds;
//IO写入 的超时秒数小于1视为不设置
protected int writeTimeoutSecond;
protected int writeTimeoutSeconds;
//最大连接数
protected int maxconns;
protected Server(long serverStartTime, String protocol, 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.protocol = protocol;
this.resourceFactory = resourceFactory;
this.prepare = servlet;
}
@@ -103,17 +121,32 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80));
this.charset = Charset.forName(config.getValue("charset", "UTF-8"));
this.maxconns = config.getIntValue("maxconns", 0);
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0);
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0);
this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30);
this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0);
this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 8 * 1024);
this.bufferCapacity = bufCapacity < 256 ? 256 : bufCapacity;
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 16);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 512);
this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 256);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 32 * 1024);
this.bufferCapacity = bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity;
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 32);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort());
if (!this.name.matches("^[a-zA-Z][\\w_-]{1,64}$")) throw new RuntimeException("server.name (" + this.name + ") is illegal");
AnyValue sslConf = config.getAnyValue("ssl");
if (sslConf != null) {
String creatorClass = sslConf.getValue("creator", SSLCreator.class.getName());
SSLCreator creator = null;
if (SSLCreator.class.getName().equals(creatorClass) || creatorClass.isEmpty()) {
creator = new SSLCreator() {
};
} else {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
creator = ((SSLCreator) classLoader.loadClass(creatorClass).getDeclaredConstructor().newInstance());
}
this.resourceFactory.inject(creator);
this.sslContext = creator.create(this, sslConf);
}
final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat();
final String n = name;
@@ -125,12 +158,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
}
protected static int parseLenth(String value, int defValue) {
if (value == null) return defValue;
value = value.toUpperCase().replace("B", "");
if (value.endsWith("G")) return Integer.decode(value.replace("G", "")) * 1024 * 1024 * 1024;
if (value.endsWith("M")) return Integer.decode(value.replace("M", "")) * 1024 * 1024;
if (value.endsWith("K")) return Integer.decode(value.replace("K", "")) * 1024;
return Integer.decode(value);
return (int) parseLenth(value, defValue + 0L);
}
protected static long parseLenth(String value, long defValue) {
@@ -142,10 +170,26 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return Long.decode(value);
}
protected static String formatLenth(long value) {
if (value < 1) return "" + value;
if (value % (1024 * 1024 * 1024) == 0) return value / (1024 * 1024 * 1024) + "G";
if (value % (1024 * 1024) == 0) return value / (1024 * 1024) + "M";
if (value % 1024 == 0) return value / (1024) + "K";
return value + "B";
}
public void destroy(final AnyValue config) throws Exception {
this.prepare.destroy(context, config);
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
public ThreadPoolExecutor getExecutor() {
return executor;
}
public InetSocketAddress getSocketAddress() {
return address;
}
@@ -170,6 +214,54 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return this.context;
}
public long getServerStartTime() {
return serverStartTime;
}
public Charset getCharset() {
return charset;
}
public int getBacklog() {
return backlog;
}
public int getBufferCapacity() {
return bufferCapacity;
}
public int getThreads() {
return threads;
}
public int getBufferPoolSize() {
return bufferPoolSize;
}
public int getResponsePoolSize() {
return responsePoolSize;
}
public int getMaxbody() {
return maxbody;
}
public int getAliveTimeoutSeconds() {
return aliveTimeoutSeconds;
}
public int getReadTimeoutSeconds() {
return readTimeoutSeconds;
}
public int getWriteTimeoutSeconds() {
return writeTimeoutSeconds;
}
public int getMaxconns() {
return maxconns;
}
public void setThreads(int threads) {
int oldthreads = this.threads;
this.context.executor.setCorePoolSize(threads);
@@ -185,17 +277,13 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public void start() throws IOException {
this.context = this.createContext();
this.prepare.init(this.context, config);
this.serverChannel = ProtocolServer.create(this.protocol, context);
this.serverChannel.open();
if (this.serverChannel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
this.serverChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
this.serverChannel.open(config);
serverChannel.bind(address, backlog);
serverChannel.setMaxconns(this.maxconns);
serverChannel.accept();
serverChannel.accept();
final String threadName = "[" + Thread.currentThread().getName() + "] ";
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", threads: " + threads + ", bufferCapacity: " + bufferCapacity + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
}
@@ -214,6 +302,14 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
}
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
this.serverClassLoader = serverClassLoader;
}
/**
* 判断是否存在Filter
*

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

@@ -5,14 +5,17 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.Supplier;
import java.util.logging.Level;
import javax.net.ssl.SSLContext;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.*;
@@ -26,56 +29,44 @@ import org.redkale.util.*;
* @author zhangjx
*/
public final class Transport {
public static final String DEFAULT_PROTOCOL = "TCP";
protected static final int MAX_POOL_LIMIT = Runtime.getRuntime().availableProcessors() * 16;
protected static final boolean supportTcpNoDelay;
static {
boolean tcpNoDelay = false;
try {
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
tcpNoDelay = channel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY);
channel.close();
} catch (Exception e) {
}
supportTcpNoDelay = tcpNoDelay;
}
protected final AtomicInteger seq = new AtomicInteger(-1);
protected final TransportFactory factory;
protected final String name; //即<group>的name属性
protected final String subprotocol; //即<group>的subprotocol属性
protected final boolean tcp;
protected final String protocol;
protected final AsynchronousChannelGroup group;
protected final InetSocketAddress clientAddress;
protected TransportAddress[] transportAddres = new TransportAddress[0];
//不可能为null
protected TransportNode[] transportNodes = new TransportNode[0];
protected final ObjectPool<ByteBuffer> bufferPool;
protected final SSLContext sslContext;
//负载均衡策略
protected final TransportStrategy strategy;
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, clientAddress, addresses, strategy);
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
}
protected Transport(String name, String protocol, String subprotocol,
final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name;
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
@@ -84,197 +75,291 @@ public final class Transport {
factory.transportReferences.add(new WeakReference<>(this));
this.tcp = "TCP".equalsIgnoreCase(protocol);
this.group = transportChannelGroup;
this.sslContext = sslContext;
this.bufferPool = transportBufferPool;
this.clientAddress = clientAddress;
this.strategy = strategy;
updateRemoteAddresses(addresses);
}
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
TransportAddress[] oldAddresses = this.transportAddres;
List<TransportAddress> list = new ArrayList<>();
if (addresses != null) {
for (InetSocketAddress addr : addresses) {
if (clientAddress != null && clientAddress.equals(addr)) continue;
list.add(new TransportAddress(addr));
final TransportNode[] oldNodes = this.transportNodes;
synchronized (this) {
List<TransportNode> list = new ArrayList<>();
if (addresses != null) {
for (InetSocketAddress addr : addresses) {
if (clientAddress != null && clientAddress.equals(addr)) continue;
boolean hasold = false;
for (TransportNode oldAddr : oldNodes) {
if (oldAddr.getAddress().equals(addr)) {
list.add(oldAddr);
hasold = true;
break;
}
}
if (hasold) continue;
list.add(new TransportNode(factory.poolmaxconns, addr));
}
}
this.transportNodes = list.toArray(new TransportNode[list.size()]);
}
this.transportAddres = list.toArray(new TransportAddress[list.size()]);
InetSocketAddress[] rs = new InetSocketAddress[oldAddresses.length];
InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length];
for (int i = 0; i < rs.length; i++) {
rs[i] = oldAddresses[i].getAddress();
rs[i] = oldNodes[i].getAddress();
}
return rs;
}
public final boolean addRemoteAddresses(final InetSocketAddress addr) {
if (addr == null) return false;
if (clientAddress != null && clientAddress.equals(addr)) return false;
synchronized (this) {
if (this.transportAddres == null) {
this.transportAddres = new TransportAddress[]{new TransportAddress(addr)};
if (this.transportNodes.length == 0) {
this.transportNodes = new TransportNode[]{new TransportNode(factory.poolmaxconns, addr)};
} else {
for (TransportAddress i : this.transportAddres) {
for (TransportNode i : this.transportNodes) {
if (addr.equals(i.address)) return false;
}
this.transportAddres = Utility.append(transportAddres, new TransportAddress(addr));
this.transportNodes = Utility.append(transportNodes, new TransportNode(factory.poolmaxconns, addr));
}
return true;
}
}
public final boolean removeRemoteAddresses(InetSocketAddress addr) {
if (addr == null) return false;
if (this.transportAddres == null) return false;
synchronized (this) {
this.transportAddres = Utility.remove(transportAddres, new TransportAddress(addr));
this.transportNodes = Utility.remove(transportNodes, new TransportNode(factory.poolmaxconns, addr));
}
return true;
}
public String getName() {
return name;
}
public String getSubprotocol() {
return subprotocol;
}
public void close() {
connPool.forEach((k, v) -> v.forEach(c -> c.dispose()));
TransportNode[] nodes = this.transportNodes;
if (nodes == null) return;
for (TransportNode node : nodes) {
if (node != null) node.dispose();
}
}
public InetSocketAddress getClientAddress() {
return clientAddress;
}
public TransportAddress[] getTransportAddresses() {
return transportAddres;
public TransportNode[] getTransportNodes() {
return transportNodes;
}
public TransportNode findTransportNode(SocketAddress addr) {
for (TransportNode node : this.transportNodes) {
if (node.address.equals(addr)) return node;
}
return null;
}
public InetSocketAddress[] getRemoteAddresses() {
InetSocketAddress[] rs = new InetSocketAddress[transportAddres.length];
InetSocketAddress[] rs = new InetSocketAddress[transportNodes.length];
for (int i = 0; i < rs.length; i++) {
rs[i] = transportAddres[i].getAddress();
rs[i] = transportNodes[i].getAddress();
}
return rs;
}
public ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> getAsyncConnectionPool() {
return connPool;
}
@Override
public String toString() {
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteAddres = " + Arrays.toString(transportAddres) + "}";
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
}
public ByteBuffer pollBuffer() {
return bufferPool.get();
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
}
public AsynchronousChannelGroup getTransportChannelGroup() {
return group;
}
public boolean isTCP() {
return tcp;
}
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr) {
if (this.strategy != null) return strategy.pollConnection(addr, this);
if (addr == null && this.transportAddres.length == 1) addr = this.transportAddres[0].address;
final boolean rand = addr == null;
if (rand && this.transportAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
if (this.strategy != null) return strategy.pollConnection(addr0, this);
final TransportNode[] nodes = this.transportNodes;
if (addr0 == null && nodes.length == 1) addr0 = nodes[0].address;
final SocketAddress addr = addr0;
final boolean rand = addr == null; //是否随机取地址
if (rand && nodes.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
try {
if (tcp) {
AsynchronousSocketChannel channel = null;
if (rand) { //取地址
TransportAddress transportAddr;
boolean tryed = false;
for (int i = 0; i < transportAddres.length; i++) {
transportAddr = transportAddres[i];
addr = transportAddr.address;
if (!transportAddr.enable) continue;
final BlockingQueue<AsyncConnection> queue = transportAddr.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) return CompletableFuture.completedFuture(conn);
}
}
tryed = true;
if (channel == null) {
channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
try {
channel.connect(addr).get(2, TimeUnit.SECONDS);
transportAddr.enable = true;
break;
} catch (Exception iex) {
transportAddr.enable = false;
channel = null;
}
}
if (channel == null && !tryed) {
for (int i = 0; i < transportAddres.length; i++) {
transportAddr = transportAddres[i];
addr = transportAddr.address;
if (channel == null) {
channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
try {
channel.connect(addr).get(2, TimeUnit.SECONDS);
transportAddr.enable = true;
break;
} catch (Exception iex) {
transportAddr.enable = false;
channel = null;
}
}
}
} else {
return AsyncConnection.createTCP(group, addr, supportTcpNoDelay, 6, 6);
}
if (channel == null) return CompletableFuture.completedFuture(null);
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, 6, 6));
} else { // UDP
if (rand) addr = this.transportAddres[0].address;
if (!tcp) { // UDP
SocketAddress udpaddr = rand ? nodes[0].address : addr;
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(addr);
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, true, 6, 6));
channel.connect(udpaddr);
return CompletableFuture.completedFuture(AsyncConnection.create(channel, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
}
if (!rand) { //指定地址
TransportNode node = findTransportNode(addr);
if (node == null) {
return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
final BlockingQueue<AsyncConnection> queue = node.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
}
}
return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
//---------------------随机取地址------------------------
int enablecount = 0;
final TransportNode[] newnodes = new TransportNode[nodes.length];
for (final TransportNode node : nodes) {
if (node.disabletime > 0) continue;
newnodes[enablecount++] = node;
}
final long now = System.currentTimeMillis();
if (enablecount > 0) { //存在可用的地址
final TransportNode one = newnodes[Math.abs(seq.incrementAndGet()) % enablecount];
final BlockingQueue<AsyncConnection> queue = one.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
}
}
CompletableFuture future = new CompletableFuture();
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
try {
channel.close();
} catch (Exception e) {
}
try {
pollConnection0(nodes, one, now).whenComplete((r, t) -> {
if (t != null) {
future.completeExceptionally(t);
} else {
future.complete(r);
}
});
} catch (Exception e) {
future.completeExceptionally(e);
}
}
});
return future;
}
return pollConnection0(nodes, null, now);
} catch (Exception ex) {
throw new RuntimeException("transport address = " + addr, ex);
}
}
private CompletableFuture<AsyncConnection> pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException {
//从可用/不可用的地址列表中创建连接
AtomicInteger count = new AtomicInteger(nodes.length);
CompletableFuture future = new CompletableFuture();
for (final TransportNode node : nodes) {
if (node == exclude) continue;
if (future.isDone()) return future;
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.connect(node.address, node, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
try {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
} catch (Exception e) {
failed(e, attachment);
}
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
if (count.decrementAndGet() < 1) {
future.completeExceptionally(exc);
}
try {
channel.close();
} catch (Exception e) {
}
}
});
}
return future;
}
public void offerConnection(final boolean forceClose, AsyncConnection conn) {
if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return;
if (!forceClose && conn.isTCP()) {
if (conn.isOpen()) {
BlockingQueue<AsyncConnection> queue = connPool.get(conn.getRemoteAddress());
if (queue == null) {
queue = new ArrayBlockingQueue<>(MAX_POOL_LIMIT);
connPool.put(conn.getRemoteAddress(), queue);
}
if (!queue.offer(conn)) conn.dispose();
TransportNode node = findTransportNode(conn.getRemoteAddress());
if (node == null || !node.conns.offer(conn)) conn.dispose();
} else {
conn.dispose();
}
} else {
conn.dispose();
}
}
public <A> void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler<Integer, A> handler) {
pollConnection(addr).whenComplete((conn, ex) -> {
if (ex != null) {
@@ -282,28 +367,28 @@ public final class Transport {
return;
}
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
buffer.clear();
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (handler != null) handler.completed(result, att);
offerBuffer(buffer);
offerConnection(false, conn);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(true, conn);
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
@@ -312,53 +397,98 @@ public final class Transport {
});
});
}
public static class TransportAddress {
public static class TransportNode {
protected InetSocketAddress address;
protected volatile long disabletime; //不可用时的时间, 为0表示可用
protected volatile boolean enable;
protected final BlockingQueue<AsyncConnection> conns = new ArrayBlockingQueue<>(MAX_POOL_LIMIT);
public TransportAddress(InetSocketAddress address) {
protected final BlockingQueue<AsyncConnection> conns;
protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<>();
public TransportNode(int poolmaxconns, InetSocketAddress address) {
this.address = address;
this.enable = true;
this.disabletime = 0;
this.conns = new ArrayBlockingQueue<>(poolmaxconns);
}
@java.beans.ConstructorProperties({"address", "enable"})
public TransportAddress(InetSocketAddress address, boolean enable) {
@ConstructorParameters({"poolmaxconns", "address", "disabletime"})
public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) {
this.address = address;
this.enable = enable;
this.disabletime = disabletime;
this.conns = new LinkedBlockingQueue<>(poolmaxconns);
}
public int getPoolmaxconns() {
return this.conns.remainingCapacity() + this.conns.size();
}
public <T> T setAttribute(String name, T value) {
attributes.put(name, value);
return value;
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name) {
return (T) attributes.get(name);
}
@SuppressWarnings("unchecked")
public <T> T removeAttribute(String name) {
return (T) attributes.remove(name);
}
public TransportNode clearAttributes() {
attributes.clear();
return this;
}
public ConcurrentHashMap<String, Object> getAttributes() {
return attributes;
}
public void setAttributes(ConcurrentHashMap<String, Object> map) {
attributes.clear();
if (map != null) attributes.putAll(map);
}
public InetSocketAddress getAddress() {
return address;
}
public boolean isEnable() {
return enable;
public long getDisabletime() {
return disabletime;
}
@ConvertColumn(ignore = true)
@ConvertDisabled
public BlockingQueue<AsyncConnection> getConns() {
return conns;
}
public void dispose() {
AsyncConnection conn;
while ((conn = conns.poll()) != null) {
conn.dispose();
}
}
@Override
public int hashCode() {
return this.address.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final TransportAddress other = (TransportAddress) obj;
final TransportNode other = (TransportNode) obj;
return this.address.equals(other.address);
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}

View File

@@ -7,7 +7,7 @@ package org.redkale.net;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
@@ -16,11 +16,13 @@ import java.util.concurrent.atomic.*;
import java.util.function.Supplier;
import java.util.logging.*;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
import org.redkale.service.Service;
import org.redkale.util.*;
/**
* System.getProperty("net.transport.pinginterval", "30") 心跳周期默认30秒
* System.getProperty("net.transport.checkinterval", "30") 检查不可用地址周期默认30秒
*
* <p>
* 详情见: https://redkale.org
@@ -29,8 +31,18 @@ import org.redkale.util.*;
*/
public class TransportFactory {
@Comment("默认TCP读取超时秒数")
public static int DEFAULT_READTIMEOUTSECONDS = 6;
@Comment("默认TCP写入超时秒数")
public static int DEFAULT_WRITETIMEOUTSECONDS = 6;
public static final String NAME_POOLMAXCONNS = "poolmaxconns";
public static final String NAME_PINGINTERVAL = "pinginterval";
public static final String NAME_CHECKINTERVAL = "checkinterval";
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
//传输端的线程池
@@ -52,11 +64,25 @@ public class TransportFactory {
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
//连接池大小
protected int poolmaxconns = Integer.getInteger("net.transport.poolmaxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍
//检查不可用地址周期, 单位:秒
protected int checkinterval = Integer.getInteger("net.transport.checkinterval", 30);
//心跳周期, 单位:秒
protected int pinginterval;
//ping的定时器
private ScheduledThreadPoolExecutor pingScheduler;
//TCP读取超时秒数
protected int readTimeoutSeconds;
//TCP写入超时秒数
protected int writeTimeoutSeconds;
//ping和检查的定时器
private ScheduledThreadPoolExecutor scheduler;
protected SSLContext sslContext;
//ping的内容
private ByteBuffer pingBuffer;
@@ -68,31 +94,49 @@ public class TransportFactory {
protected final TransportStrategy strategy;
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
final TransportStrategy strategy) {
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
this.executor = executor;
this.bufferPool = bufferPool;
this.channelGroup = channelGroup;
this.sslContext = sslContext;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.strategy = strategy;
}
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
this(executor, bufferPool, channelGroup, null);
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
this(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
if (conf != null) {
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, 0);
this.poolmaxconns = conf.getIntValue(NAME_POOLMAXCONNS, this.poolmaxconns);
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, this.pinginterval);
this.checkinterval = conf.getIntValue(NAME_CHECKINTERVAL, this.checkinterval);
if (this.poolmaxconns < 2) this.poolmaxconns = 2;
if (this.pinginterval < 2) this.pinginterval = 2;
if (this.checkinterval < 2) this.checkinterval = 2;
}
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryTask-Thread");
t.setDaemon(true);
return t;
});
this.scheduler.scheduleAtFixedRate(() -> {
try {
checks();
} catch (Throwable t) {
logger.log(Level.SEVERE, "TransportFactory schedule(interval=" + checkinterval + "s) check error", t);
}
}, checkinterval, checkinterval, TimeUnit.SECONDS);
if (this.pinginterval > 0) {
if (this.pingScheduler == null && pingBuffer != null) {
if (pingBuffer != null) {
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
this.pongLength = pongLength;
this.pingScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryPingTask-Thread");
t.setDaemon(true);
return t;
});
pingScheduler.scheduleAtFixedRate(() -> {
scheduler.scheduleAtFixedRate(() -> {
pings();
}, pinginterval, pinginterval, TimeUnit.SECONDS);
}
@@ -100,10 +144,14 @@ public class TransportFactory {
}
public static TransportFactory create(int threads) {
return create(threads, threads * 2, 8 * 1024);
return create(threads, threads * 2, 8 * 1024, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity) {
return create(threads, bufferPoolSize, bufferCapacity, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity, int readTimeoutSeconds, int writeTimeoutSeconds) {
final ObjectPool<ByteBuffer> transportPool = new ObjectPool<>(new AtomicLong(), new AtomicLong(), bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
@@ -123,29 +171,48 @@ public class TransportFactory {
} catch (IOException e) {
throw new RuntimeException(e);
}
return create(transportExec, transportPool, transportGroup);
return create(transportExec, transportPool, transportGroup, readTimeoutSeconds, writeTimeoutSeconds);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
return new TransportFactory(executor, bufferPool, channelGroup, null);
return new TransportFactory(executor, bufferPool, channelGroup, null, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, strategy);
int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, SSLContext sslContext) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, "TCP", "", this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
return new Transport(name, "TCP", "", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, "", this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
return new Transport(name, protocol, "", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, String subprotocol,
final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, subprotocol, this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
return new Transport(name, protocol, subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public String findGroupName(InetSocketAddress addr) {
@@ -226,14 +293,14 @@ public class TransportFactory {
}
if (info == null) info = new TransportGroupInfo("TCP");
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, sncpAddress, addresses, this.strategy);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) {
if (groupName == null) return null;
TransportGroupInfo info = groupInfos.get(groupName);
if (info == null) return null;
return new Transport(groupName, info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, sncpAddress, info.addresses, this.strategy);
return new Transport(groupName, info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, info.addresses, this.strategy);
}
public ExecutorService getExecutor() {
@@ -267,7 +334,7 @@ public class TransportFactory {
}
public void shutdownNow() {
if (this.pingScheduler != null) this.pingScheduler.shutdownNow();
if (this.scheduler != null) this.scheduler.shutdownNow();
try {
this.channelGroup.shutdownNow();
} catch (Exception e) {
@@ -275,8 +342,7 @@ public class TransportFactory {
}
}
private void pings() {
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
private void checks() {
List<WeakReference> nulllist = new ArrayList<>();
for (WeakReference<Transport> ref : transportReferences) {
Transport transport = ref.get();
@@ -284,8 +350,39 @@ public class TransportFactory {
nulllist.add(ref);
continue;
}
List<BlockingQueue<AsyncConnection>> list = new ArrayList<>(transport.getAsyncConnectionPool().values());
for (final BlockingQueue<AsyncConnection> queue : list) {
Transport.TransportNode[] nodes = transport.getTransportNodes();
for (final Transport.TransportNode node : nodes) {
if (node.disabletime < 1) continue; //可用
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(transport.group);
channel.connect(node.address, node, new CompletionHandler<Void, Transport.TransportNode>() {
@Override
public void completed(Void result, Transport.TransportNode attachment) {
attachment.disabletime = 0;
}
@Override
public void failed(Throwable exc, Transport.TransportNode attachment) {
attachment.disabletime = System.currentTimeMillis();
}
});
} catch (Exception e) {
}
}
}
for (WeakReference ref : nulllist) {
transportReferences.remove(ref);
}
}
private void pings() {
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
for (WeakReference<Transport> ref : transportReferences) {
Transport transport = ref.get();
if (transport == null) continue;
Transport.TransportNode[] nodes = transport.getTransportNodes();
for (final Transport.TransportNode node : nodes) {
final BlockingQueue<AsyncConnection> queue = node.conns;
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作
@@ -337,9 +434,6 @@ public class TransportFactory {
}
}
}
for (WeakReference ref : nulllist) {
transportReferences.remove(ref);
}
}
private static boolean checkName(String name) { //不能含特殊字符

View File

@@ -18,5 +18,25 @@ import java.util.concurrent.CompletableFuture;
*/
public interface TransportStrategy {
/**
* 创建AsyncConnection
*
* @param addr 服务器地址
* @param transport Transport
*
* @return AsyncConnection
*/
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr, Transport transport);
/**
* 回收AsyncConnection返回false表示使用Transport默认的回收实现 返回true表示自定义回收实现
*
* @param forceClose 是否强制关闭
* @param conn AsyncConnection
*
* @return boolean
*/
default boolean offerConnection(final boolean forceClose, AsyncConnection conn) {
return false;
}
}

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,14 @@
*/
package org.redkale.net.http;
import java.net.*;
import java.nio.*;
import java.nio.ByteBuffer;
import org.redkale.asm.MethodDebugVisitor;
import java.nio.channels.CompletionHandler;
import java.nio.charset.*;
import java.security.*;
import java.util.concurrent.*;
import java.util.logging.*;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import java.util.function.*;
import org.redkale.asm.*;
import static org.redkale.asm.Opcodes.*;
import org.redkale.net.*;
import org.redkale.util.*;
@@ -31,12 +30,8 @@ public class HttpContext extends Context {
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
public HttpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset,
address, prepare, readTimeoutSecond, writeTimeoutSecond);
public HttpContext(HttpContextConfig config) {
super(config);
random.setSeed(Math.abs(System.nanoTime()));
}
@@ -54,6 +49,21 @@ public class HttpContext extends Context {
return responsePool;
}
@Override
protected Consumer<ByteBuffer> getBufferConsumer() {
return super.getBufferConsumer();
}
@Override
protected void offerBuffer(ByteBuffer buffer) {
super.offerBuffer(buffer);
}
@Override
protected void offerBuffer(ByteBuffer... buffers) {
super.offerBuffer(buffers);
}
@SuppressWarnings("unchecked")
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
@@ -69,6 +79,7 @@ public class HttpContext extends Context {
//生成规则与SncpAsyncHandler.Factory 很类似
//-------------------------------------------------------------
final boolean handlerinterface = handlerClass.isInterface();
final String cpDesc = Type.getDescriptor(ConstructorParameters.class);
final String handlerClassName = handlerClass.getName().replace('.', '/');
final String handlerName = CompletionHandler.class.getName().replace('.', '/');
final String handlerDesc = Type.getDescriptor(CompletionHandler.class);
@@ -76,7 +87,7 @@ public class HttpContext extends Context {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor mv;
MethodDebugVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName} : new String[]{handlerName});
@@ -85,10 +96,10 @@ public class HttpContext extends Context {
fv.visitEnd();
}
{//构造方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
//mv.setDebug(true);
{
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
av0 = mv.visitAnnotation(cpDesc, true);
{
AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, "handler");
@@ -108,7 +119,7 @@ public class HttpContext extends Context {
for (java.lang.reflect.Method method : handlerClass.getMethods()) { //
if ("completed".equals(method.getName()) && method.getParameterCount() == 2) {
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
mv.visitVarInsn(ALOAD, 1);
@@ -118,7 +129,7 @@ public class HttpContext extends Context {
mv.visitMaxs(3, 3);
mv.visitEnd();
} else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) {
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
mv.visitVarInsn(ALOAD, 1);
@@ -128,7 +139,7 @@ public class HttpContext extends Context {
mv.visitMaxs(3, 3);
mv.visitEnd();
} else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) {
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
Class returnType = method.getReturnType();
if (returnType == void.class) {
mv.visitInsn(RETURN);
@@ -165,4 +176,8 @@ public class HttpContext extends Context {
}.loadClass(newDynName.replace('/', '.'), bytes);
return (Creator<H>) Creator.create(newHandlerClazz);
}
public static class HttpContextConfig extends ContextConfig {
}
}

View File

@@ -45,6 +45,8 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
private BiPredicate<String, String>[] forbidURIPredicates; //禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致
final List<HttpRender> renders = new ArrayList<>();
private List<HttpServlet> removeHttpServlet(final Predicate<MappingEntry> predicateEntry, final Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter) {
List<HttpServlet> servlets = new ArrayList<>();
synchronized (allMapStrings) {
@@ -124,7 +126,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
List<HttpServlet> list = removeHttpServlet(predicateEntry, predicateFilter);
return list == null || list.isEmpty() ? null : list.get(0);
}
@SuppressWarnings("unchecked")
public <T extends WebSocket> HttpServlet removeHttpServlet(Class<T> websocketOrServletType) {
Predicate<MappingEntry> predicateEntry = (t) -> {
Class type = t.servlet.getClass();
@@ -142,6 +144,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
return list == null || list.isEmpty() ? null : list.get(0);
}
@SuppressWarnings("unchecked")
public boolean addForbidURIReg(final String urlreg) {
if (urlreg == null || urlreg.isEmpty()) return false;
synchronized (excludeLock) {
@@ -167,6 +170,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
}
}
@SuppressWarnings("unchecked")
public boolean removeForbidURIReg(final String urlreg) {
if (urlreg == null || urlreg.isEmpty()) return false;
synchronized (excludeLock) {
@@ -196,6 +200,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
}
@Override
@SuppressWarnings("unchecked")
public void init(HttpContext context, AnyValue config) {
super.init(context, config); //必须要执行
Collection<HttpServlet> servlets = getServlets();
@@ -203,31 +208,54 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
s.preInit(context, getServletConf(s));
s.init(context, getServletConf(s));
});
AnyValue resConfig = config.getAnyValue("resource-servlet");
if ((resConfig instanceof DefaultAnyValue) && resConfig.getValue("webroot", "").isEmpty()) {
((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root"));
}
if (resConfig == null) { //主要用于嵌入式的HttpServer初始化
DefaultAnyValue dresConfig = new DefaultAnyValue();
dresConfig.addValue("webroot", config.getValue("root"));
dresConfig.addValue("ranges", config.getValue("ranges"));
dresConfig.addValue("cache", config.getAnyValue("cache"));
AnyValue[] rewrites = config.getAnyValues("rewrite");
if (rewrites != null) {
for (AnyValue rewrite : rewrites) {
dresConfig.addValue("rewrite", rewrite);
}
{ //设置ResourceServlet
AnyValue resConfig = config.getAnyValue("resource-servlet");
if ((resConfig instanceof DefaultAnyValue) && resConfig.getValue("webroot", "").isEmpty()) {
((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root"));
}
resConfig = dresConfig;
if (resConfig == null) { //主要用于嵌入式的HttpServer初始化
DefaultAnyValue dresConfig = new DefaultAnyValue();
dresConfig.addValue("webroot", config.getValue("root"));
dresConfig.addValue("ranges", config.getValue("ranges"));
dresConfig.addValue("cache", config.getAnyValue("cache"));
AnyValue[] rewrites = config.getAnyValues("rewrite");
if (rewrites != null) {
for (AnyValue rewrite : rewrites) {
dresConfig.addValue("rewrite", rewrite);
}
}
resConfig = dresConfig;
}
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
try {
this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).getDeclaredConstructor().newInstance();
} catch (Throwable e) {
this.resourceHttpServlet = new HttpResourceServlet();
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
}
context.getResourceFactory().inject(this.resourceHttpServlet);
this.resourceHttpServlet.init(context, resConfig);
}
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
try {
this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).newInstance();
} catch (Throwable e) {
this.resourceHttpServlet = new HttpResourceServlet();
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
{ //设置TemplateEngine
AnyValue[] renderConfigs = config.getAnyValues("render");
if (renderConfigs != null) {
for (AnyValue renderConfig : renderConfigs) {
String renderType = renderConfig.getValue("value");
try {
HttpRender render = (HttpRender) Thread.currentThread().getContextClassLoader().loadClass(renderType).getDeclaredConstructor().newInstance();
for (HttpRender one : renders) {
if (one.getType().equals(render.getType())) throw new RuntimeException("HttpRender(" + renderType + ") repeat");
}
context.getResourceFactory().inject(render);
render.init(context, renderConfig);
renders.add(render);
} catch (Throwable e) {
logger.log(Level.WARNING, "init HttpRender(" + renderType + ") error", e);
}
}
Collections.sort(renders, (o1, o2) -> o1.getType().isAssignableFrom(o2.getType()) ? 1 : -1);
}
}
this.resourceHttpServlet.init(context, resConfig);
}
@Override

View File

@@ -0,0 +1,38 @@
/*
* 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.http;
import org.redkale.convert.Convert;
import org.redkale.util.AnyValue;
/**
* HTTP输出引擎的基类 <br>
* HttpRender主要是给HttpResponse.finish(Object obj)提供指定数据类型的输出策略。 <br>
* <pre>
* HttpResponse.finish(Object obj)内置对如下数据类型进行了特殊处理:
* CharSequence/String
* byte[]
* ByteBuffer
* ByteBuffer[]
* File
* HttpResult
* </pre>
* <p>
* 如果对其他数据类型有特殊输出的需求则需要自定义HttpRender。
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 泛型
*/
public interface HttpRender<T> {
public void init(HttpContext context, AnyValue config);
public <V extends T> void renderTo(HttpRequest request, HttpResponse response, Convert convert, V scope);
public Class<T> getType();
}

View File

@@ -9,7 +9,7 @@ import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.charset.Charset;
import java.nio.charset.*;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
@@ -31,8 +31,6 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
*/
public class HttpRequest extends Request<HttpContext> {
protected static final Charset UTF8 = Charset.forName("UTF-8");
public static final String SESSIONID_NAME = "JSESSIONID";
@Comment("Method GET/POST/...")
@@ -134,15 +132,19 @@ public class HttpRequest extends Request<HttpContext> {
String value = array.toString(index, array.size() - index, charset).trim();
switch (name) {
case "Content-Type":
case "content-type":
this.contentType = value;
break;
case "Content-Length":
case "content-length":
this.contentLength = Long.decode(value);
break;
case "Host":
case "host":
this.host = value;
break;
case "Cookie":
case "cookie":
if (this.cookie == null || this.cookie.isEmpty()) {
this.cookie = value;
} else {
@@ -150,8 +152,14 @@ public class HttpRequest extends Request<HttpContext> {
}
break;
case "Connection":
case "connection":
this.connection = value;
this.setKeepAlive(!"close".equalsIgnoreCase(value));
if (context.getAliveTimeoutSeconds() >= 0) {
this.setKeepAlive(!"close".equalsIgnoreCase(value));
}
break;
case "user-agent":
header.addValue("User-Agent", value);
break;
default:
header.addValue(name, value);
@@ -332,7 +340,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public String getBodyUTF8() {
return array.toString(UTF8);
return array.toString(StandardCharsets.UTF_8);
}
/**
@@ -344,7 +352,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public <T> T getBodyJson(java.lang.reflect.Type type) {
String str = array.toString(UTF8);
String str = array.toString(StandardCharsets.UTF_8);
if (str == null || str.isEmpty()) return null;
return context.getJsonConvert().convertFrom(type, str);
}
@@ -359,7 +367,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public <T> T getBodyJson(JsonConvert convert, java.lang.reflect.Type type) {
String str = array.toString(UTF8);
String str = array.toString(StandardCharsets.UTF_8);
if (str == null || str.isEmpty()) return null;
return convert.convertFrom(type, str);
}
@@ -626,7 +634,11 @@ public class HttpRequest extends Request<HttpContext> {
public short getRequstURILastPath(short defvalue) {
String val = getRequstURILastPath();
if (val.isEmpty()) return defvalue;
return val.isEmpty() ? defvalue : Short.parseShort(val);
try {
return Short.parseShort(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -642,7 +654,11 @@ public class HttpRequest extends Request<HttpContext> {
public short getRequstURILastPath(int radix, short defvalue) {
String val = getRequstURILastPath();
if (val.isEmpty()) return defvalue;
return val.isEmpty() ? defvalue : Short.parseShort(val, radix);
try {
return Short.parseShort(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -656,7 +672,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public int getRequstURILastPath(int defvalue) {
String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Integer.parseInt(val);
try {
return val.isEmpty() ? defvalue : Integer.parseInt(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -671,7 +691,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public int getRequstURILastPath(int radix, int defvalue) {
String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Integer.parseInt(val, radix);
try {
return val.isEmpty() ? defvalue : Integer.parseInt(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -685,7 +709,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public float getRequstURILastPath(float defvalue) {
String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Float.parseFloat(val);
try {
return val.isEmpty() ? defvalue : Float.parseFloat(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -699,7 +727,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public long getRequstURILastPath(long defvalue) {
String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Long.parseLong(val);
try {
return val.isEmpty() ? defvalue : Long.parseLong(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -714,7 +746,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public long getRequstURILastPath(int radix, long defvalue) {
String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Long.parseLong(val, radix);
try {
return val.isEmpty() ? defvalue : Long.parseLong(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -728,7 +764,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public double getRequstURILastPath(double defvalue) {
String val = getRequstURILastPath();
return val.isEmpty() ? defvalue : Double.parseDouble(val);
try {
return val.isEmpty() ? defvalue : Double.parseDouble(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -775,7 +815,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public short getRequstURIPath(String prefix, short defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Short.parseShort(val);
try {
return val == null ? defvalue : Short.parseShort(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -791,7 +835,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public short getRequstURIPath(int radix, String prefix, short defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Short.parseShort(val, radix);
try {
return val == null ? defvalue : Short.parseShort(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -807,7 +855,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public int getRequstURIPath(String prefix, int defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Integer.parseInt(val);
try {
return val == null ? defvalue : Integer.parseInt(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -824,7 +876,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public int getRequstURIPath(int radix, String prefix, int defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Integer.parseInt(val, radix);
try {
return val == null ? defvalue : Integer.parseInt(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -839,7 +895,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public float getRequstURIPath(String prefix, float defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Float.parseFloat(val);
try {
return val == null ? defvalue : Float.parseFloat(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -854,7 +914,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public long getRequstURIPath(String prefix, long defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Long.parseLong(val);
try {
return val == null ? defvalue : Long.parseLong(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -870,7 +934,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public long getRequstURIPath(int radix, String prefix, long defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Long.parseLong(val, radix);
try {
return val == null ? defvalue : Long.parseLong(val, radix);
} catch (NumberFormatException e) {
return defvalue;
}
}
/**
@@ -885,7 +953,11 @@ public class HttpRequest extends Request<HttpContext> {
*/
public double getRequstURIPath(String prefix, double defvalue) {
String val = getRequstURIPath(prefix, null);
return val == null ? defvalue : Double.parseDouble(val);
try {
return val == null ? defvalue : Double.parseDouble(val);
} catch (NumberFormatException e) {
return defvalue;
}
}
//------------------------------------------------------------------------------
@@ -1422,10 +1494,13 @@ public class HttpRequest extends Request<HttpContext> {
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
if (flipper == null) {
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
int limit = getRequstURIPath("limit:", maxLimit);
int limit = getRequstURIPath("limit:", 0);
int offset = getRequstURIPath("offset:", 0);
String sort = getRequstURIPath("sort:", "");
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, sort);
if (limit > 0) {
if (limit > maxLimit) limit = maxLimit;
flipper = new org.redkale.source.Flipper(limit, offset, sort);
}
} else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) {
flipper.setLimit(maxLimit);
}

View File

@@ -59,7 +59,8 @@ public class HttpResourceServlet extends HttpServlet {
final String uri = path.toString().substring(rootstr.length()).replace('\\', '/');
//logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event");
if (event.kind() == ENTRY_DELETE) {
files.remove(uri);
FileEntry en = files.remove(uri);
if (en != null) en.remove();
} else if (event.kind() == ENTRY_MODIFY) {
FileEntry en = files.get(uri);
if (en != null && en.file != null) {
@@ -317,10 +318,8 @@ public class HttpResourceServlet extends HttpServlet {
}
}
@Override
protected void finalize() throws Throwable {
public void remove() {
if (this.content != null) this.servlet.cachedLength.add(0L - this.content.remaining());
super.finalize();
}
public long getCachedLength() {

View File

@@ -11,10 +11,12 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.*;
import java.text.*;
import java.time.ZoneId;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
@@ -34,32 +36,26 @@ import org.redkale.util.*;
*/
public class HttpResponse extends Response<HttpContext, HttpRequest> {
/**
* HttpResponse.finish 方法内调用
* 主要给@HttpCacheable使用
*/
protected static interface BufferHandler {
public ByteBuffer[] execute(final HttpResponse response, final ByteBuffer[] buffers);
}
private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\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[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes();
private static final Set<OpenOption> options = new HashSet<>();
protected static final byte[] connectCloseBytes = "Connection: close\r\n".getBytes();
private static final DateFormat GMT_DATE_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH);
protected static final byte[] connectAliveBytes = "Connection: keep-alive\r\n".getBytes();
private static final Set<OpenOption> options = new HashSet<>();
private static final Map<Integer, String> httpCodes = new HashMap<>();
static {
options.add(StandardOpenOption.READ);
GMT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
httpCodes.put(100, "Continue");
httpCodes.put(101, "Switching Protocols");
@@ -107,9 +103,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
httpCodes.put(505, "HTTP Version Not Supported");
}
private static final ZoneId ZONE_GMT = ZoneId.of("GMT");
private int status = 200;
private String contentType = "text/plain; charset=utf-8";
private String contentType = "";
private long contentLength = -1;
@@ -117,9 +115,17 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private boolean headsended = false;
private BufferHandler 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 String[][] defaultAddHeaders;
@@ -128,19 +134,35 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private final boolean autoOptions;
private final HttpCookie defcookie;
private final Supplier<byte[]> dateSupplier;
private final HttpCookie defaultCookie;
private final List<HttpRender> renders;
private final boolean hasRender;
private final HttpRender onlyoneHttpRender;
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle());
}
public HttpResponse(HttpContext context, HttpRequest request, String[][] defaultAddHeaders, String[][] defaultSetHeaders,
HttpCookie defcookie, boolean autoOptions) {
public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) {
super(context, request);
this.defaultAddHeaders = defaultAddHeaders;
this.defaultSetHeaders = defaultSetHeaders;
this.defcookie = defcookie;
this.autoOptions = autoOptions;
this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes();
this.defaultAddHeaders = config.defaultAddHeaders;
this.defaultSetHeaders = config.defaultSetHeaders;
this.defaultCookie = config.defaultCookie;
this.autoOptions = config.autoOptions;
this.dateSupplier = config.dateSupplier;
this.renders = config.renders;
this.hasRender = renders != null && !renders.isEmpty();
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
this.contentType = this.plainContentType;
}
@Override
@@ -154,7 +176,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override
protected boolean recycle() {
boolean rs = super.recycle();
this.status = 200;
this.contentLength = -1;
this.contentType = null;
@@ -162,7 +183,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.headsended = false;
this.header.clear();
this.bufferHandler = null;
return rs;
return super.recycle();
}
@Override
@@ -200,6 +221,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return this.autoOptions;
}
@Override
protected void offerBuffer(ByteBuffer... buffers) {
super.offerBuffer(buffers);
}
/**
* 增加Cookie值
*
@@ -233,7 +259,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return Utility.createAsyncHandler((v, a) -> {
finish(v);
}, (t, a) -> {
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
finish(500, null);
});
}
@@ -254,15 +280,24 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
}
/**
* 获取ByteBuffer生成器
*
* @return ByteBuffer生成器
*/
public Supplier<ByteBuffer> getBufferSupplier() {
return getBodyBufferSupplier();
}
/**
* 将对象以JSON格式输出
*
* @param obj 输出对象
*/
public void finishJson(final Object obj) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = obj;
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), obj));
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), obj));
}
/**
@@ -272,9 +307,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param objs 输出对象
*/
public void finishMapJson(final Object... objs) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs;
finish(request.getJsonConvert().convertMapTo(context.getBufferSupplier(), objs));
finish(request.getJsonConvert().convertMapTo(getBodyBufferSupplier(), objs));
}
/**
@@ -284,9 +319,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象
*/
public void finishJson(final JsonConvert convert, final Object obj) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = obj;
finish(convert.convertTo(context.getBufferSupplier(), obj));
finish(convert.convertTo(getBodyBufferSupplier(), obj));
}
/**
@@ -297,9 +332,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param objs 输出对象
*/
public void finishMapJson(final JsonConvert convert, final Object... objs) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs;
finish(convert.convertMapTo(context.getBufferSupplier(), objs));
finish(convert.convertMapTo(getBodyBufferSupplier(), objs));
}
/**
@@ -309,9 +344,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象
*/
public void finishJson(final Type type, final Object obj) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
this.output = obj;
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), type, obj));
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), type, obj));
}
/**
@@ -322,9 +357,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param obj 输出对象
*/
public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = obj;
finish(convert.convertTo(context.getBufferSupplier(), type, obj));
finish(convert.convertTo(getBodyBufferSupplier(), type, obj));
}
/**
@@ -333,9 +368,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param objs 输出对象
*/
public void finishJson(final Object... objs) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs;
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), objs));
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), objs));
}
/**
@@ -344,13 +379,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param ret RetResult输出对象
*/
public void finishJson(final org.redkale.service.RetResult ret) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
this.header.addValue("retinfo", ret.getRetinfo());
}
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), ret));
finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), ret));
}
/**
@@ -360,13 +395,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param ret RetResult输出对象
*/
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
this.contentType = "text/plain; charset=utf-8";
this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
this.header.addValue("retinfo", ret.getRetinfo());
}
finish(convert.convertTo(context.getBufferSupplier(), ret));
finish(convert.convertTo(getBodyBufferSupplier(), ret));
}
/**
@@ -454,7 +489,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
try {
finish((File) obj);
} catch (IOException e) {
getContext().getLogger().log(Level.WARNING, "HttpServlet finish File occur, forece to close channel. request = " + getRequest(), e);
context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, forece to close channel. request = " + getRequest(), e);
finish(500, null);
}
} else if (obj instanceof HttpResult) {
@@ -463,11 +498,33 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) {
finish("");
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else {
finish(convert, result.getResult());
}
} else {
if (convert instanceof TextConvert) this.contentType = "text/plain; charset=utf-8";
if (hasRender) {
if (onlyoneHttpRender != null) {
if (onlyoneHttpRender.getType().isAssignableFrom(obj.getClass())) {
onlyoneHttpRender.renderTo(this.request, this, convert, obj);
return;
}
} else {
Class objt = obj.getClass();
for (HttpRender render : this.renders) {
if (render.getType().isAssignableFrom(objt)) {
render.renderTo(this.request, this, convert, obj);
return;
}
}
}
}
if (convert instanceof JsonConvert) {
this.contentType = this.jsonContentType;
} else if (convert instanceof TextConvert) {
this.contentType = this.plainContentType;
}
if (this.recycleListener != null) this.output = obj;
if (obj instanceof org.redkale.service.RetResult) {
org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj;
@@ -475,8 +532,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo());
}
}
ByteBuffer[] buffers = type == null ? convert.convertTo(context.getBufferSupplier(), obj)
: convert.convertTo(context.getBufferSupplier(), type, obj);
ByteBuffer[] buffers = type == null ? convert.convertTo(getBodyBufferSupplier(), obj)
: convert.convertTo(getBodyBufferSupplier(), type, obj);
finish(buffers);
}
}
@@ -498,7 +555,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
}
if (context.getCharset() == null) {
if (bufferHandler != null) {
bufferHandler.execute(this, new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(obj))});
bufferHandler.apply(this, new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(obj))});
}
final char[] chars = Utility.charArray(obj);
this.contentLength = Utility.encodeUTF8Length(chars);
@@ -513,7 +570,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} else {
ByteBuffer buffer = context.getCharset().encode(obj);
if (bufferHandler != null) {
ByteBuffer[] bufs = bufferHandler.execute(this, new ByteBuffer[]{buffer});
ByteBuffer[] bufs = bufferHandler.apply(this, new ByteBuffer[]{buffer});
if (bufs != null) buffer = bufs[0];
}
this.contentLength = buffer.remaining();
@@ -558,8 +615,27 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override
public void finish(final byte[] bs) {
if (isClosed()) return; //避免重复关闭
if (this.context.getBufferCapacity() == bs.length) {
ByteBuffer buffer = this.context.pollBuffer();
if (this.context.getBufferCapacity() >= bs.length) {
ByteBuffer buffer = getBodyBufferSupplier().get();
buffer.put(bs);
buffer.flip();
this.finish(false, buffer);
} else {
this.finish(false, ByteBuffer.wrap(bs));
}
}
/**
* 将指定byte[]按响应结果输出
*
* @param contentType ContentType
* @param bs 输出内容
*/
public void finish(final String contentType, final byte[] bs) {
if (isClosed()) return; //避免重复关闭
this.contentType = contentType;
if (this.context.getBufferCapacity() >= bs.length) {
ByteBuffer buffer = getBodyBufferSupplier().get();
buffer.put(bs);
buffer.flip();
this.finish(false, buffer);
@@ -621,7 +697,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public void finish(boolean kill, ByteBuffer... buffers) {
if (isClosed()) return; //避免重复关闭
if (bufferHandler != null) {
ByteBuffer[] bufs = bufferHandler.execute(this, buffers);
ByteBuffer[] bufs = bufferHandler.apply(this, buffers);
if (bufs != null) buffers = bufs;
}
if (kill) refuseAlive();
@@ -801,20 +877,26 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.channel.write(hbuffer, hbuffer, new TransferFileHandler(file, offset, length));
}
private ByteBuffer createHeader() {
//Header大小不能超过一个ByteBuffer的容量
protected ByteBuffer createHeader() {
this.headsended = true;
ByteBuffer buffer = this.context.pollBuffer();
buffer.put(("HTTP/1.1 " + this.status + " " + (this.status == 200 ? "OK" : httpCodes.get(this.status)) + "\r\n").getBytes());
buffer.put(("Content-Type: " + (this.contentType == null ? "text/plain; charset=utf-8" : this.contentType) + "\r\n").getBytes());
if (this.contentLength >= 0) {
buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
ByteBuffer buffer = this.pollWriteReadBuffer();
if (this.status == 200) {
buffer.put(status200Bytes);
} else {
buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes());
}
if (!this.request.isKeepAlive()) {
buffer.put("Connection: close\r\n".getBytes());
if (this.contentLength >= 0) buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
if (this.contentType == this.jsonContentType) {
buffer.put(this.jsonContentTypeBytes);
} else if (this.contentType == null || this.contentType == this.plainContentType) {
buffer.put(this.plainContentTypeBytes);
} else {
buffer.put(("Content-Type: " + (this.contentType == null ? this.plainContentType : this.contentType) + "\r\n").getBytes());
}
buffer.put(serverNameBytes);
if (dateSupplier != null) buffer.put(dateSupplier.get());
buffer.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes);
if (this.defaultAddHeaders != null) {
for (String[] headers : this.defaultAddHeaders) {
@@ -846,13 +928,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
}
if (request.newsessionid != null) {
String domain = defcookie == null ? null : defcookie.getDomain();
String domain = defaultCookie == null ? null : defaultCookie.getDomain();
if (domain == null) {
domain = "";
} else {
domain = "Domain=" + domain + "; ";
}
String path = defcookie == null ? null : defcookie.getPath();
String path = defaultCookie == null ? null : defaultCookie.getPath();
if (path == null || path.isEmpty()) path = "/";
if (request.newsessionid.isEmpty()) {
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes());
@@ -863,9 +945,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (this.cookies != null) {
for (HttpCookie cookie : this.cookies) {
if (cookie == null) continue;
if (defcookie != null) {
if (defcookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defcookie.getDomain());
if (defcookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defcookie.getPath());
if (defaultCookie != null) {
if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain());
if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath());
}
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
}
@@ -882,9 +964,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist());
if (cookie.getMaxAge() > 0) {
sb.append("; Max-Age=").append(cookie.getMaxAge());
synchronized (GMT_DATE_FORMAT) {
sb.append("; Expires=").append(GMT_DATE_FORMAT.format(new Date(System.currentTimeMillis() + cookie.getMaxAge() * 1000)));
}
sb.append("; Expires=").append(RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(ZONE_GMT).plusSeconds(cookie.getMaxAge())));
}
if (cookie.getSecure()) sb.append("; Secure");
if (cookie.isHttpOnly()) sb.append("; HttpOnly");
@@ -1015,7 +1095,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*
* @return 拦截器
*/
protected BufferHandler getBufferHandler() {
protected BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> getBufferHandler() {
return bufferHandler;
}
@@ -1024,7 +1104,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*
* @param bufferHandler 拦截器
*/
protected void setBufferHandler(BufferHandler bufferHandler) {
protected void setBufferHandler(BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> bufferHandler) {
this.bufferHandler = bufferHandler;
}
@@ -1105,7 +1185,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
getContext().offerBuffer(attachment);
context.offerBuffer(attachment);
finish(true);
try {
filechannel.close();
@@ -1114,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;
}
public HttpResult(String contentType, T result) {
this.contentType = contentType;
this.result = result;
}
public HttpResult<T> header(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value));

View File

@@ -0,0 +1,100 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this referid file, choose Tools | Templates
* and open the referid in the editor.
*/
package org.redkale.net.http;
import java.util.*;
import java.util.function.BiConsumer;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
/**
* HTTP输出引擎的对象域 <br>
* 输出引擎的核心类, 业务开发人员只有通过本类对象才能调用到输出引擎功能。 <br>
* <p>
* HttpServlet调用: <br>
* <pre>
* &#064;HttpMapping(url = "/hello.html", auth = false)
* public void hello(HttpRequest req, HttpResponse resp) throws IOException {
* resp.finish(HttpScope.refer("/hello.html").attr("content", "哈哈"));
* }
* </pre>
* <p>
* RestService调用: <br>
* <pre>
* &#064;RestMapping(name = "hello.html", auth = false)
* public HttpScope hello() {
* return HttpScope.refer("hello.html").attr("content", "哈哈");
* }
* </pre>
*
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class HttpScope {
protected String referid;
protected Map<String, Object> attributes;
public static HttpScope refer(String template) {
HttpScope rs = new HttpScope();
rs.setReferid(template);
return rs;
}
public HttpScope attr(Map<String, Object> map) {
if (map == null) return this;
if (this.attributes == null) this.attributes = new LinkedHashMap<>();
this.attributes.putAll(map);
return this;
}
public HttpScope attr(String name, Object value) {
if (this.attributes == null) this.attributes = new LinkedHashMap<>();
this.attributes.put(name, value);
return this;
}
@SuppressWarnings("unchecked")
public <T> T find(String name) {
return this.attributes == null ? null : (T) this.attributes.get(name);
}
@SuppressWarnings("unchecked")
public <T> T find(HttpScope parent, String name) {
T rs = this.attributes == null ? null : (T) this.attributes.get(name);
if (rs != null) return rs;
return parent == null ? null : parent.find(name);
}
public void forEach(BiConsumer<String, Object> action) {
if (this.attributes == null) return;
this.attributes.forEach(action);
}
public String getReferid() {
return referid;
}
public void setReferid(String referid) {
this.referid = referid;
}
public Map<String, Object> getAttributes() {
return attributes;
}
@ConvertDisabled(type = ConvertType.JSON)
public void setAttributes(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -8,10 +8,17 @@ package org.redkale.net.http;
import java.lang.reflect.Field;
import java.net.HttpCookie;
import java.nio.ByteBuffer;
import java.text.*;
import java.time.ZoneId;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Level;
import org.redkale.net.*;
import org.redkale.net.http.HttpContext.HttpContextConfig;
import org.redkale.net.http.HttpResponse.HttpResponseConfig;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.Service;
import org.redkale.util.*;
@@ -26,12 +33,20 @@ import org.redkale.util.*;
*/
public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpResponse, HttpServlet> {
private ScheduledThreadPoolExecutor dateScheduler;
private byte[] currDateBytes;
public HttpServer() {
this(System.currentTimeMillis());
this(System.currentTimeMillis(), ResourceFactory.root());
}
public HttpServer(long serverStartTime) {
super(serverStartTime, "TCP", new HttpPrepareServlet());
public HttpServer(ResourceFactory resourceFactory) {
this(System.currentTimeMillis(), resourceFactory);
}
public HttpServer(long serverStartTime, ResourceFactory resourceFactory) {
super(serverStartTime, "TCP", resourceFactory, new HttpPrepareServlet());
}
@Override
@@ -39,6 +54,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
super.init(config);
}
@Override
public void destroy(final AnyValue config) throws Exception {
super.destroy(config);
if (this.dateScheduler != null) {
this.dateScheduler.shutdownNow();
this.dateScheduler = null;
}
}
public List<HttpServlet> getHttpServlets() {
return this.prepare.getServlets();
}
@@ -47,6 +71,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
return this.prepare.getFilters();
}
/**
* 获取HttpRender列表
*
* @return HttpRender列表
*/
public List<HttpRender> getHttpRenders() {
return ((HttpPrepareServlet) this.prepare).renders;
}
/**
* 获取静态资源HttpServlet
*
@@ -282,7 +315,9 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
final List<String[]> defaultAddHeaders = new ArrayList<>();
final List<String[]> defaultSetHeaders = new ArrayList<>();
boolean autoOptions = false;
int datePeriod = 0;
String plainContentType = null;
String jsonContentType = null;
HttpCookie defaultCookie = null;
String remoteAddrHeader = null;
if (config != null) {
@@ -301,6 +336,11 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
AnyValue resps = config == null ? null : config.getAnyValue("response");
if (resps != null) {
AnyValue contenttypes = resps.getAnyValue("contenttype");
if (contenttypes != null) {
plainContentType = contenttypes.getValue("plain");
jsonContentType = contenttypes.getValue("json");
}
AnyValue[] addHeaders = resps.getAnyValues("addheader");
if (addHeaders.length > 0) {
for (AnyValue addHeader : addHeaders) {
@@ -347,22 +387,74 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
}
AnyValue options = resps == null ? null : resps.getAnyValue("options");
autoOptions = options != null && options.getBoolValue("auto", false);
AnyValue dates = resps == null ? null : resps.getAnyValue("date");
datePeriod = dates == null ? 0 : dates.getIntValue("period", 0);
}
}
final String[][] addHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
final String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
final boolean options = autoOptions;
Supplier<byte[]> dateSupplier = null;
if (datePeriod == 0) {
final ZoneId gmtZone = ZoneId.of("GMT");
dateSupplier = () -> ("Date: " + RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(gmtZone)) + "\r\n").getBytes();
} else if (datePeriod > 0) {
if (this.dateScheduler == null) {
this.dateScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, "HTTP:" + port + "-DateSchedule-Thread");
t.setDaemon(true);
return t;
});
final DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM y HH:mm:ss z", Locale.ENGLISH);
gmtDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes();
final int dp = datePeriod;
this.dateScheduler.scheduleAtFixedRate(() -> {
try {
currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes();
} catch (Throwable t) {
logger.log(Level.SEVERE, "HttpServer schedule(interval=" + dp + "ms) date-format error", t);
}
}, 1000 - System.currentTimeMillis() % 1000, datePeriod, TimeUnit.MILLISECONDS);
dateSupplier = () -> currDateBytes;
}
}
final HttpCookie defCookie = defaultCookie;
final String addrHeader = remoteAddrHeader;
final HttpResponseConfig respConfig = new HttpResponseConfig();
respConfig.plainContentType = plainContentType;
respConfig.jsonContentType = jsonContentType;
respConfig.defaultAddHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
respConfig.defaultSetHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
respConfig.defaultCookie = defaultCookie;
respConfig.autoOptions = autoOptions;
respConfig.dateSupplier = dateSupplier;
respConfig.renders = ((HttpPrepareServlet) prepare).renders;
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, rcapacity, bufferPool, responsePool,
this.maxbody, this.charset, this.address, this.prepare, this.readTimeoutSecond, this.writeTimeoutSecond);
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie, options));
final HttpContextConfig contextConfig = new HttpContextConfig();
contextConfig.serverStartTime = this.serverStartTime;
contextConfig.logger = this.logger;
contextConfig.executor = this.executor;
contextConfig.sslContext = this.sslContext;
contextConfig.bufferCapacity = rcapacity;
contextConfig.bufferPool = bufferPool;
contextConfig.responsePool = responsePool;
contextConfig.maxconns = this.maxconns;
contextConfig.maxbody = this.maxbody;
contextConfig.charset = this.charset;
contextConfig.address = this.address;
contextConfig.prepare = this.prepare;
contextConfig.resourceFactory = this.resourceFactory;
contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds;
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
HttpContext httpcontext = new HttpContext(contextConfig);
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), respConfig));
return httpcontext;
}
}

View File

@@ -10,9 +10,10 @@ import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import java.util.function.BiFunction;
import org.redkale.asm.*;
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
import static org.redkale.asm.Opcodes.*;
import org.redkale.net.*;
import org.redkale.service.RetResult;
import org.redkale.util.*;
@@ -206,11 +207,11 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
//------------------------------------------------------------------------------
final String supDynName = HttpServlet.class.getName().replace('.', '/');
final String interName = this.getClass().getName().replace('.', '/');
final String interDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(this.getClass());
final String requestSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Request.class);
final String responseSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Response.class);
final String requestDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpRequest.class);
final String responseDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpResponse.class);
final String interDesc = org.redkale.asm.Type.getDescriptor(this.getClass());
final String requestSupDesc = org.redkale.asm.Type.getDescriptor(Request.class);
final String responseSupDesc = org.redkale.asm.Type.getDescriptor(Response.class);
final String requestDesc = org.redkale.asm.Type.getDescriptor(HttpRequest.class);
final String responseDesc = org.redkale.asm.Type.getDescriptor(HttpResponse.class);
String newDynName = interName + "_Dyn_" + method.getName();
int i = 0;
for (;;) {
@@ -273,7 +274,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
HttpServlet instance = (HttpServlet) newClazz.newInstance();
HttpServlet instance = (HttpServlet) newClazz.getDeclaredConstructor().newInstance();
instance.getClass().getField(factfield).set(instance, this);
return instance;
} catch (Exception ex) {
@@ -293,7 +294,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
this.ignore = mapping == null || !mapping.auth();
this.cacheseconds = mapping == null ? 0 : mapping.cacheseconds();
this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null;
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
int status = response.getStatus();
if (status != 200) return null;
@@ -315,7 +316,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
return false;
}
public final HttpResponse.BufferHandler cacheHandler;
public final BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> cacheHandler;
public final ConcurrentHashMap<String, CacheEntry> cache;

View File

@@ -70,6 +70,7 @@ public class MimeType {
contentTypes.put("jpeg", "image/jpeg");
contentTypes.put("jpg", "image/jpeg");
contentTypes.put("js", "text/javascript");
contentTypes.put("json", "application/json");
contentTypes.put("kar", "audio/x-midi");
contentTypes.put("latex", "application/x-latex");
contentTypes.put("log", "text/plain");

View File

@@ -26,8 +26,6 @@ public final class MultiContext {
private static final Logger logger = Logger.getLogger(MultiContext.class.getSimpleName());
private static final Charset UTF8 = Charset.forName("UTF-8");
private final String contentType;
private final InputStream in;
@@ -58,7 +56,7 @@ public final class MultiContext {
};
public MultiContext(final Charset charsetName, final String contentType, final DefaultAnyValue params, final InputStream in, String fielnameRegex) {
this.charset = charsetName == null ? UTF8 : charsetName;
this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName;
this.contentType = contentType == null ? "" : contentType.trim();
this.parameters = params;
this.boundary = parseBoundary(this.contentType);
@@ -112,6 +110,19 @@ public final class MultiContext {
return tmpfile;
}
/**
* 根据临时文件获取上传时的文件名
*
* @param file 临时文件
*
* @return 上传的文件名
*/
public static String getFileName(File file) {
if (file == null) return null;
String name = file.getName();
return name.startsWith("redkale-") ? name.substring(name.indexOf('_') + 1) : name;
}
//或被 REST 用到
/**
* 获取第一个文件
@@ -133,9 +144,9 @@ public final class MultiContext {
has = true;
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
File file = new File(home, "tmp/redkale_" + System.nanoTime() + "/" + part.getFilename());
File file = new File(home, "tmp/redkale-" + System.nanoTime() + "_" + part.getFilename());
File parent = file.getParentFile();
parent.mkdirs();
if (!parent.isDirectory()) parent.mkdirs();
boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file);
if (!rs) {
file.delete();
@@ -165,9 +176,9 @@ public final class MultiContext {
for (MultiPart part : parts()) {
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
File file = new File(home, "tmp/redkale_" + System.nanoTime() + "/" + part.getFilename());
File file = new File(home, "tmp/redkale-" + System.nanoTime() + "_" + part.getFilename());
File parent = file.getParentFile();
parent.mkdirs();
if (!parent.isDirectory()) parent.mkdirs();
boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file);
if (!rs) {
file.delete();

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.net.http;
import org.redkale.asm.MethodDebugVisitor;
import java.io.*;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@@ -14,12 +15,13 @@ import java.nio.channels.CompletionHandler;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Resource;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type;
import org.redkale.convert.Convert;
import org.redkale.asm.*;
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
import static org.redkale.asm.Opcodes.*;
import org.redkale.asm.Type;
import org.redkale.convert.*;
import org.redkale.convert.json.*;
import org.redkale.net.Cryptor;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.source.Flipper;
@@ -78,7 +80,7 @@ public final class Rest {
private Rest() {
}
public static class MethodParamClassVisitor extends ClassVisitor {
static class MethodParamClassVisitor extends ClassVisitor {
private final Map<String, List<String>> fieldmap;
@@ -91,8 +93,10 @@ public final class Rest {
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (java.lang.reflect.Modifier.isStatic(access)) return null;
List<String> fieldnames = new ArrayList<>();
fieldmap.put(name + ":" + desc, fieldnames);
return new MethodVisitor(Opcodes.ASM5) {
String key = name + ":" + desc;
if (fieldmap.containsKey(key)) return null;
fieldmap.put(key, fieldnames);
return new MethodVisitor(Opcodes.ASM6) {
@Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
if (index < 1) return;
@@ -110,22 +114,23 @@ public final class Rest {
}
//返回的List中参数列表可能会比方法参数量多因为方法内的临时变量也会存入list中 所以需要list的元素集合比方法的参数多
public static Map<String, List<String>> getMethodParamNames(Class clazz) {
public static Map<String, List<String>> getMethodParamNames(Map<String, List<String>> map, Class clazz) {
String n = clazz.getName();
InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class");
Map<String, List<String>> map = new HashMap<>();
if (in == null) return map;
try {
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM5, map), 0);
} catch (Exception e) { //无需理会
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM6, map), 0);
} catch (Exception e) { //无需理会
}
return map;
Class superClass = clazz.getSuperclass();
if (superClass == Object.class) return map;
return getMethodParamNames(map, superClass);
}
}
static JsonConvert createJsonConvert(RestConvert[] converts) {
if (converts == null || converts.length < 1) return JsonConvert.root();
final JsonFactory childFactory = JsonFactory.root().createChild();
final JsonFactory childFactory = JsonFactory.create();
List<Class> types = new ArrayList<>();
for (RestConvert rc : converts) {
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
@@ -147,14 +152,14 @@ public final class Rest {
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
if (controller.ignore()) return null;
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
}
static String getWebModuleName(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "");
if (controller.ignore()) return null;
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "");
return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", "");
}
static boolean isRestDyn(HttpServlet servlet) {
@@ -183,7 +188,7 @@ public final class Rest {
}
}
static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) {
public static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) {
if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet");
if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet");
@@ -198,8 +203,8 @@ public final class Rest {
}
if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet");
final String rwsname = ResourceFactory.formatResourceName(rws.name());
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
//----------------------------------------------------------------------------------------
final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
@@ -232,20 +237,29 @@ public final class Rest {
final String resourceGenericDescriptor = sb1.length() == sb2.length() ? null : sb2.toString();
//----------------------------------------------------------------------------------------
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(webSocketType);
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), webSocketType);
final Set<String> messageNames = new HashSet<>();
final List<Method> messageMethods = new ArrayList<>();
for (Method method : webSocketType.getMethods()) {
RestOnMessage rom = method.getAnnotation(RestOnMessage.class);
if (rom == null) continue;
String name = rom.name();
if (Modifier.isFinal(method.getModifiers())) throw new RuntimeException("@RestOnMessage method can not final but (" + method + ")");
if (Modifier.isStatic(method.getModifiers())) throw new RuntimeException("@RestOnMessage method can not static but (" + method + ")");
if (method.getReturnType() != void.class) throw new RuntimeException("@RestOnMessage method must return void but (" + method + ")");
if (method.getExceptionTypes().length > 0) throw new RuntimeException("@RestOnMessage method can not throw exception but (" + method + ")");
if (name.isEmpty()) throw new RuntimeException(method + " RestOnMessage.name is empty createRestWebSocketServlet");
if (messageNames.contains(name)) throw new RuntimeException(method + " repeat RestOnMessage.name(" + name + ") createRestWebSocketServlet");
messageNames.add(name);
messageMethods.add(method);
}
//----------------------------------------------------------------------------------------
final String resDesc = Type.getDescriptor(Resource.class);
final String wsDesc = Type.getDescriptor(WebSocket.class);
final String wsParamDesc = Type.getDescriptor(WebSocketParam.class);
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
final String convertDisabledDesc = Type.getDescriptor(ConvertDisabled.class);
final String webSocketParamName = Type.getInternalName(WebSocketParam.class);
final String supDynName = WebSocketServlet.class.getName().replace('.', '/');
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String webSocketInternalName = Type.getInternalName(webSocketType);
@@ -264,7 +278,7 @@ public final class Rest {
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor mv;
MethodDebugVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
@@ -306,7 +320,7 @@ public final class Rest {
java.lang.reflect.Type fieldType = field.getGenericType();
fv = cw.visitField(ACC_PRIVATE, "_redkale_resource_" + i, Type.getDescriptor(field.getType()), fieldType == field.getType() ? null : Utility.getTypeDescriptor(fieldType), null);
{
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0 = fv.visitAnnotation(resDesc, true);
av0.visit("name", res.name());
av0.visitEnd();
}
@@ -314,7 +328,7 @@ public final class Rest {
}
}
{ //_DynWebSocketServlet构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitVarInsn(ALOAD, 0);
@@ -346,7 +360,7 @@ public final class Rest {
mv.visitEnd();
}
{ //createWebSocket 方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()Lorg/redkale/net/http/WebSocket;", "<G::Ljava/io/Serializable;T:Ljava/lang/Object;>()Lorg/redkale/net/http/WebSocket<TG;TT;>;", null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()" + wsDesc, "<G::Ljava/io/Serializable;T:Ljava/lang/Object;>()L" + WebSocket.class.getName().replace('.', '/') + "<TG;TT;>;", null));
mv.visitTypeInsn(NEW, newDynName + "$" + newDynWebSokcetSimpleName);
mv.visitInsn(DUP);
for (int i = 0; i < resourcesFields.size(); i++) {
@@ -359,7 +373,7 @@ public final class Rest {
mv.visitEnd();
}
{ //createRestOnMessageConsumer
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;>;", null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", null));
mv.visitTypeInsn(NEW, newDynConsumerFullName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, newDynConsumerFullName, "<init>", "()V", false);
@@ -368,7 +382,7 @@ public final class Rest {
mv.visitEnd();
}
{ //resourceName
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null));
mv.visitLdcInsn(rwsname);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
@@ -377,19 +391,19 @@ public final class Rest {
RestClassLoader newLoader = new RestClassLoader(loader);
for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage List
for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage 子消息List
Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", null);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", new String[]{webSocketParamName, "java/lang/Runnable"});
cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
Set<String> paramnames = new HashSet<>();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc);
Parameter[] params = method.getParameters();
for (int j = 0; j < params.length; j++) {
final LinkedHashMap<String, Parameter> paramap = new LinkedHashMap(); //必须使用LinkedHashMap确保顺序
for (int j = 0; j < params.length; j++) { //字段列表
Parameter param = params[j];
String paramname = param.getName();
RestParam rp = param.getAnnotation(RestParam.class);
@@ -400,23 +414,97 @@ public final class Rest {
}
if (paramnames.contains(paramname)) throw new RuntimeException(method + " has same @RestParam.name");
paramnames.add(paramname);
paramap.put(paramname, param);
fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()),
param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null);
fv.visitEnd();
}
{ //构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
{ //_redkale_websocket
fv = cw2.visitField(ACC_PUBLIC, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";", null, null);
av0 = fv.visitAnnotation(convertDisabledDesc, true);
av0.visitEnd();
fv.visitEnd();
}
{ //空构造函数
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{ //toString
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
{ //getNames
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getNames", "()[Ljava/lang/String;", null, null));
pushInt(mv, paramap.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int index = -1;
for (Map.Entry<String, Parameter> en : paramap.entrySet()) {
mv.visitInsn(DUP);
pushInt(mv, ++index);
mv.visitLdcInsn(en.getKey());
mv.visitInsn(AASTORE);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(paramap.size() + 2, 1);
mv.visitEnd();
}
{ //getValue
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/String;)Ljava/lang/Object;", "<T:Ljava/lang/Object;>(Ljava/lang/String;)TT;", null));
for (Map.Entry<String, Parameter> en : paramap.entrySet()) {
Class paramType = en.getValue().getType();
mv.visitLdcInsn(en.getKey());
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, en.getKey(), Type.getDescriptor(paramType));
if (paramType.isPrimitive()) {
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(paramType, 1), 0).getClass();
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(paramType) + ")" + Type.getDescriptor(bigclaz), false);
}
mv.visitInsn(ARETURN);
mv.visitLabel(l1);
}
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{ //execute
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(L" + newDynWebSokcetFullName + ";)V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/convert/json/JsonConvert", "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";");
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(method.getAnnotation(RestOnMessage.class).name());
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, "preOnMessage", "(Ljava/lang/String;" + wsParamDesc + "Ljava/lang/Runnable;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(4, 2);
mv.visitEnd();
}
{ //run
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "run", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";");
for (Map.Entry<String, Parameter> en : paramap.entrySet()) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), en.getKey(), Type.getDescriptor(en.getValue().getType()));
}
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
{ //toString
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
@@ -440,7 +528,7 @@ public final class Rest {
fv.visitEnd();
}
{ //构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
@@ -448,10 +536,10 @@ public final class Rest {
mv.visitEnd();
}
{ //toString
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/convert/json/JsonConvert", "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
@@ -467,7 +555,7 @@ public final class Rest {
cw2.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC);
{
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "(" + resourceDescriptor + ")V", resourceGenericDescriptor == null ? null : ("(" + resourceGenericDescriptor + ")V"), null));
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "(" + resourceDescriptor + ")V", resourceGenericDescriptor == null ? null : ("(" + resourceGenericDescriptor + ")V"), null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, webSocketInternalName, "<init>", "()V", false);
for (int i = 0; i < resourcesFields.size(); i++) {
@@ -486,7 +574,7 @@ public final class Rest {
{ //_DynRestOnMessageConsumer class
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"});
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"});
cw2.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC);
cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC);
@@ -497,7 +585,7 @@ public final class Rest {
}
{ //构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
@@ -506,7 +594,7 @@ public final class Rest {
}
{ //accept函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;)V", null, null));
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", null, null));
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, newDynWebSokcetFullName);
mv.visitVarInsn(ASTORE, 3);
@@ -519,32 +607,16 @@ public final class Rest {
final Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
final String messagename = method.getAnnotation(RestOnMessage.class).name();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc);
Parameter[] params = method.getParameters();
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
Label ifLabel = new Label();
mv.visitJumpInsn(IFNULL, ifLabel);
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
mv.visitVarInsn(ALOAD, 3);
for (int j = 0; j < params.length; j++) {
Parameter param = params[j];
String paramname = param.getName();
RestParam rp = param.getAnnotation(RestParam.class);
if (rp != null && !rp.name().isEmpty()) {
paramname = rp.name();
} else if (names != null && names.size() > j) {
paramname = names.get(j);
}
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), paramname, Type.getDescriptor(param.getType()));
}
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false);
mv.visitMethodInsn(INVOKEVIRTUAL, (newDynMessageFullName + endfix), "execute", "(L" + newDynWebSokcetFullName + ";)V", false);
mv.visitInsn(RETURN);
mv.visitLabel(ifLabel);
}
@@ -553,13 +625,13 @@ public final class Rest {
mv.visitEnd();
}
{//虚拟accept函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null));
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, "org/redkale/net/http/WebSocket");
mv.visitTypeInsn(CHECKCAST, WebSocket.class.getName().replace('.', '/'));
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, "java/lang/Object");
mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;)V", false);
mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
@@ -570,13 +642,20 @@ public final class Rest {
cw.visitEnd();
Class<?> newClazz = newLoader.loadClass(newDynName.replace('/', '.'), cw.toByteArray());
try {
return (T) newClazz.newInstance();
T servlet = (T) newClazz.getDeclaredConstructor().newInstance();
if (rws.cryptor() != Cryptor.class) {
Cryptor cryptor = rws.cryptor().getDeclaredConstructor().newInstance();
Field cryptorField = newClazz.getSuperclass().getDeclaredField("cryptor"); //WebSocketServlet
cryptorField.setAccessible(true);
cryptorField.set(servlet, cryptor);
}
return servlet;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static <T extends HttpServlet> T createRestServlet(final ClassLoader classLoader, final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) {
public static <T extends HttpServlet> T createRestServlet(final ClassLoader classLoader, final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) {
if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet");
if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet");
int mod = baseServletType.getModifiers();
@@ -585,6 +664,7 @@ public final class Rest {
final String serviceDesc = Type.getDescriptor(serviceType);
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String resDesc = Type.getDescriptor(Resource.class);
final String reqDesc = Type.getDescriptor(HttpRequest.class);
final String respDesc = Type.getDescriptor(HttpResponse.class);
final String convertDesc = Type.getDescriptor(Convert.class);
@@ -595,6 +675,8 @@ public final class Rest {
final String flipperDesc = Type.getDescriptor(Flipper.class);
final String httprsDesc = Type.getDescriptor(HttpResult.class);
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
final String multiContextDesc = Type.getDescriptor(MultiContext.class);
final String multiContextName = MultiContext.class.getName().replace('.', '/');
final String mappingDesc = Type.getDescriptor(HttpMapping.class);
final String webparamDesc = Type.getDescriptor(HttpParam.class);
final String webparamsDesc = Type.getDescriptor(HttpParam.HttpParams.class);
@@ -619,11 +701,11 @@ public final class Rest {
final String defmodulename = getWebModuleNameLowerCase(serviceType);
final String bigmodulename = getWebModuleName(serviceType);
final String catalog = controller == null ? "" : controller.catalog();
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor mv;
MethodDebugVisitor mv;
AnnotationVisitor av0;
Map<String, Object> classMap = new LinkedHashMap<>();
List<Map<String, Object>> mappingMaps = new ArrayList<>();
@@ -638,15 +720,83 @@ public final class Rest {
av0.visit("value", Type.getType(Type.getDescriptor(serviceType)));
av0.visitEnd();
}
final List<MappingEntry> entrys = new ArrayList<>();
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
//获取所有可以转换成HttpMapping的方法
int methodidex = 0;
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
for (final Method method : serviceType.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
if (method.isSynthetic()) continue;
if (EXCLUDERMETHODS.contains(method.getName())) continue;
if ("init".equals(method.getName())) continue;
if ("destroy".equals(method.getName())) continue;
if ("version".equals(method.getName())) continue;
if (controller == null) continue;
RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class);
if (!controller.automapping() && mappings.length < 1) continue;
boolean ignore = false;
for (RestMapping mapping : mappings) {
if (mapping.ignore()) {
ignore = true;
break;
}
}
if (ignore) continue;
Class[] extypes = method.getExceptionTypes();
if (extypes.length > 1) {
if (mappings != null && mappings.length > 0) throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method with throws IOException");
continue;
}
if (extypes.length == 1 && extypes[0] != IOException.class) {
if (mappings != null && mappings.length > 0) throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method with throws IOException");
continue;
}
paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType));
if (mappings.length == 0) { //没有Mapping设置一个默认值
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
} else {
for (RestMapping mapping : mappings) {
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
}
}
methodidex++;
}
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
{ //注入 @WebServlet 注解
String urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*";
String urlpath = "";
int moduleid = controller == null ? 0 : controller.moduleid();
boolean repair = controller == null ? true : controller.repair();
String comment = controller == null ? "" : controller.comment();
av0 = cw.visitAnnotation(webServletDesc, true);
{
AnnotationVisitor av1 = av0.visitArray("value");
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();
}
av0.visit("moduleid", moduleid);
@@ -662,14 +812,14 @@ public final class Rest {
{ //注入 @Resource private XXXService _service;
fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0 = fv.visitAnnotation(resDesc, true);
av0.visit("name", "");
av0.visitEnd();
fv.visitEnd();
}
{ //注入 @Resource(name = "APP_HOME") private File _redkale_home;
fv = cw.visitField(ACC_PRIVATE, "_redkale_home", Type.getDescriptor(File.class), null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0 = fv.visitAnnotation(resDesc, true);
av0.visit("name", "APP_HOME");
av0.visitEnd();
fv.visitEnd();
@@ -683,7 +833,7 @@ public final class Rest {
fv.visitEnd();
}
{ //构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
@@ -692,49 +842,8 @@ public final class Rest {
mv.visitEnd();
}
final List<MappingEntry> entrys = new ArrayList<>();
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
//获取所有可以转换成HttpMapping的方法
int methodidex = 0;
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
for (final Method method : serviceType.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
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(method.getGenericParameterTypes());
if (mappings.length == 0) { //没有Mapping设置一个默认值
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
} else {
for (RestMapping mapping : mappings) {
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
}
}
methodidex++;
}
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType);
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
final List<RestConvert[]> restConverts = new ArrayList<>();
@@ -749,7 +858,7 @@ public final class Rest {
final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class);
if (rcs != null && rcs.length > 0) restConverts.add(rcs);
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
//mv.setDebug(true);
mv.debugLine();
@@ -909,7 +1018,7 @@ public final class Rest {
}
}
av0 = mv.visitAnnotation(mappingDesc, true);
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/" + entry.name + (reqpath ? "/" : "");
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : "");
av0.visit("url", url);
av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds);
@@ -922,7 +1031,7 @@ public final class Rest {
}
av1.visitEnd();
java.lang.reflect.Type grt = method.getGenericReturnType();
java.lang.reflect.Type grt = TypeToken.getGenericType(method.getGenericReturnType(), serviceType);
av0.visit("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
av0.visitEnd();
@@ -956,36 +1065,39 @@ public final class Rest {
av1.visitEnd();
av0.visitEnd();
}
int uploadLocal = 0;
if (mupload != null) { //存在文件上传
if (muploadType == byte[].class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false);
mv.visitLdcInsn(mupload.maxLength());
mv.visitLdcInsn(mupload.fileNameReg());
mv.visitLdcInsn(mupload.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
mv.visitVarInsn(ASTORE, maxLocals);
uploadLocal = maxLocals;
} else if (muploadType == File.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(mupload.maxLength());
mv.visitLdcInsn(mupload.fileNameReg());
mv.visitLdcInsn(mupload.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
mv.visitVarInsn(ASTORE, maxLocals);
uploadLocal = maxLocals;
} else if (muploadType == File[].class) { //File[]
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(mupload.maxLength());
mv.visitLdcInsn(mupload.fileNameReg());
mv.visitLdcInsn(mupload.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
mv.visitVarInsn(ASTORE, maxLocals);
uploadLocal = maxLocals;
}
maxLocals++;
}
@@ -1402,7 +1514,7 @@ public final class Rest {
} while ((loop = loop.getSuperclass()) != Object.class);
if (!attrParaNames.isEmpty()) { //参数存在 RestHeader、RestCookie、RestSessionid、RestAddress、RestBody字段
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); //加载JsonBean
Label lif = new Label();
mv.visitJumpInsn(IFNULL, lif); //if(bean != null) {
for (Map.Entry<String, Object[]> en : attrParaNames.entrySet()) {
@@ -1410,12 +1522,22 @@ public final class Rest {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, en.getKey(), attrDesc);
mv.visitVarInsn(ALOAD, maxLocals);
boolean upload = en.getKey().contains("_upload");
mv.visitVarInsn(ALOAD, upload ? (maxLocals - 1) : 1);
mv.visitVarInsn(ALOAD, en.getKey().contains("_upload") ? uploadLocal : 1);
if (en.getKey().contains("_header_")) {
mv.visitLdcInsn(en.getValue()[0].toString());
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
String headerkey = en.getValue()[0].toString();
if ("Host".equalsIgnoreCase(headerkey)) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHost", "()Ljava/lang/String;", false);
} else if ("Content-Type".equalsIgnoreCase(headerkey)) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getContentType", "()Ljava/lang/String;", false);
} else if ("Connection".equalsIgnoreCase(headerkey)) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getConnection", "()Ljava/lang/String;", false);
} else if ("Method".equalsIgnoreCase(headerkey)) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMethod", "()Ljava/lang/String;", false);
} else {
mv.visitLdcInsn(headerkey);
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
}
} else if (en.getKey().contains("_cookie_")) {
mv.visitLdcInsn(en.getValue()[0].toString());
mv.visitLdcInsn("");
@@ -1592,7 +1714,7 @@ public final class Rest {
//classMap.put("mappings", mappingMaps); //不显示太多信息
{ //toString函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
//mv.setDebug(true);
mv.visitLdcInsn(JsonConvert.root().convertTo(classMap));
mv.visitInsn(ARETURN);
@@ -1603,7 +1725,7 @@ public final class Rest {
cw.visitEnd();
Class<?> newClazz = new RestClassLoader(loader).loadClass(newDynName.replace('/', '.'), cw.toByteArray());
try {
T obj = ((Class<T>) newClazz).newInstance();
T obj = ((Class<T>) newClazz).getDeclaredConstructor().newInstance();
for (Map.Entry<String, org.redkale.util.Attribute> en : restAttributes.entrySet()) {
Field attrField = newClazz.getDeclaredField(en.getKey());
attrField.setAccessible(true);
@@ -1642,7 +1764,7 @@ public final class Rest {
return true;
}
private static void pushInt(AsmMethodVisitor mv, int num) {
private static void pushInt(MethodDebugVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
@@ -1706,6 +1828,22 @@ public final class Rest {
this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds();
this.comment = mapping.comment();
boolean pound = false;
Parameter[] params = method.getParameters();
for (Parameter param : params) {
RestParam rp = param.getAnnotation(RestParam.class);
if (rp != null && !rp.name().isEmpty() && rp.name().charAt(0) == '#') {
pound = true;
break;
}
}
if (!pound && params.length == 1) {
Class ptype = method.getParameterTypes()[0];
if (this.name.startsWith("find") || this.name.startsWith("delete")) {
if (ptype.isPrimitive() || ptype == String.class) pound = true;
}
}
this.existsPound = pound;
}
public final int methodidx; // _paramtypes 的下标从0开始
@@ -1726,6 +1864,8 @@ public final class Rest {
public final int cacheseconds;
public final boolean existsPound; //是否包含#的参数
@RestMapping()
void mapping() { //用于获取Mapping 默认值
}

View File

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

View File

@@ -10,7 +10,12 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 标记在RestWebSocket的接收消息方法上
* 标记在RestWebSocket的接收消息方法上; <br>
* 注意:被标记的方法必须同时符合以下条件: <br>
* 1、必须修饰为public
* 2、不能修饰为final和static
* 3、返回值必须是void
* 4、不能throws检查型异常
*
* <br><p>
* 详情见: https://redkale.org

View File

@@ -46,7 +46,7 @@ public @interface RestParam {
int radix() default 10;
/**
* 参数是否必传
* 参数是否必传, 仅供apidoc功能使用框架运行中不作验证
*
* @return boolean
*/

View File

@@ -8,6 +8,7 @@ package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import org.redkale.net.Cryptor;
/**
* 只能依附在WebSocket类上name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl的默认路径为 hello)。 <br>
@@ -53,7 +54,7 @@ public @interface RestWebSocket {
boolean single() default true;
/**
* WebSocket.createUserid返回的值是否不能表示户登录态
* WebSocket.createUserid返回的值是否不能表示户登录态 比如createUserid返回随机的UUID那么anyuser应该为true
*
* @return 默认false
*/
@@ -66,6 +67,13 @@ public @interface RestWebSocket {
*/
int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
/**
* 加密解密器
*
* @return Cryptor
*/
Class<? extends Cryptor> cryptor() default Cryptor.class;
/**
* 最大连接数, 小于1表示无限制
*
@@ -73,12 +81,19 @@ public @interface RestWebSocket {
*/
int wsmaxconns() default 0;
/**
* 操作WebSocketNode对应CacheSource并发数, 为-1表示无限制为0表示系统默认值(CPU*8)
*
* @return 最大连接数
*/
int wsthreads() default 0;
/**
* 最大消息体长度, 小于1表示无限制
*
* @return 最大消息体长度
*/
int wsmaxbody() default 16 * 1024;
int wsmaxbody() default 32 * 1024;
/**
* 是否屏蔽该类的转换

View File

@@ -369,6 +369,18 @@ public abstract class WebSocket<G extends Serializable, T> {
return broadcastMessage((Convert) null, message, true);
}
/**
* 广播消息, 给所有人发消息
*
* @param wsrange 过滤条件
* @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message) {
return broadcastMessage((WebSocketRange) null, (Convert) null, message, true);
}
/**
* 广播消息, 给所有人发消息
*
@@ -381,6 +393,19 @@ public abstract class WebSocket<G extends Serializable, T> {
return broadcastMessage(convert, message, true);
}
/**
* 广播消息, 给所有人发消息
*
* @param wsrange 过滤条件
* @param convert Convert
* @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) {
return broadcastMessage((WebSocketRange) null, convert, message, true);
}
/**
* 广播消息, 给所有人发消息
*
@@ -393,6 +418,19 @@ public abstract class WebSocket<G extends Serializable, T> {
return broadcastMessage((Convert) null, message, last);
}
/**
* 广播消息, 给所有人发消息
*
* @param wsrange 过滤条件
* @param message 消息内容
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) {
return broadcastMessage(wsrange, (Convert) null, message, last);
}
/**
* 广播消息, 给所有人发消息
*
@@ -403,11 +441,25 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message, final boolean last) {
return broadcastMessage((WebSocketRange) null, convert, message, last);
}
/**
* 广播消息, 给所有人发消息
*
* @param wsrange 过滤条件
* @param convert Convert
* @param message 消息内容
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message, final boolean last) {
if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL);
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(convert, json, last));
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(wsrange, convert, json, last));
}
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(convert, message, last);
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(wsrange, convert, message, last);
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("broadcast send websocket message(" + message + ")");
return rs;
}
@@ -609,6 +661,17 @@ public abstract class WebSocket<G extends Serializable, T> {
*/
protected abstract CompletableFuture<G> createUserid();
/**
* WebSocket.broadcastMessage时的过滤条件
*
* @param wsrange 过滤条件
*
* @return boolean
*/
protected boolean predicate(WebSocketRange wsrange) {
return true;
}
/**
* WebSokcet连接成功后的回调方法
*/
@@ -631,6 +694,19 @@ public abstract class WebSocket<G extends Serializable, T> {
public void onPong(byte[] bytes) {
}
/**
*
* 接收到消息前的拦截方法, ping/pong不在其内 <br>
* 注意:处理完后需要调用 messageEvent.run() 才能响应onMessage
*
* @param restmapping Rest的方法名没有则为空字符串
* @param param onMessage方法的参数
* @param messageEvent onMessage事件
*/
public void preOnMessage(String restmapping, WebSocketParam param, Runnable messageEvent) {
messageEvent.run();
}
/**
* 接收到消息的回调方法
*
@@ -678,11 +754,12 @@ public abstract class WebSocket<G extends Serializable, T> {
}
/**
* 当Single模式下用户重复登陆时回调函数 默认处理逻辑关闭之前的WebSocket连接
* 当Single模式下用户重复登陆时回调函数默认处理方式: 关闭旧连接
*
* @return Future 可以为null, 为null或者Future值为false表示关闭新连接 Future值为true表示关闭旧连接
*/
public void onSingleRepeatConnect() {
this._engine.node.forceCloseWebSocket(getUserid());
public CompletableFuture<Boolean> onSingleRepeatConnect() {
return forceCloseWebSocket(getUserid()).thenApply((r) -> true);
}
/**
@@ -703,6 +780,15 @@ public abstract class WebSocket<G extends Serializable, T> {
return this._runner == null ? 0 : this._runner.lastSendTime;
}
/**
* 获取最后一次读取消息的时间
*
* @return long
*/
public long getLastReadTime() {
return this._runner == null ? 0 : this._runner.lastReadTime;
}
/**
* 获取最后一次发送PING消息的时间
*
@@ -716,7 +802,7 @@ public abstract class WebSocket<G extends Serializable, T> {
* 显式地关闭WebSocket
*/
public final void close() {
if (this._runner != null) this._runner.closeRunner(CLOSECODE_FORCED);
if (this._runner != null) this._runner.closeRunner(CLOSECODE_FORCED, "user close");
}
/**

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